Skip to content

Commit 33a82db

Browse files
committed
feat: add file-io-basics core modules for csharp go and python
1 parent 446508a commit 33a82db

24 files changed

Lines changed: 689 additions & 13 deletions

File tree

LANGUAGE_PARITY_MATRIX.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ This matrix tracks concept parity across C++, C#, Go, and Python.
2525

2626
Current parity progress in non-C++ tracks:
2727

28-
- C#: `2/6` modules complete
29-
- Go: `2/6` modules complete
30-
- Python: `2/6` modules complete
28+
- C#: `3/6` modules complete
29+
- Go: `3/6` modules complete
30+
- Python: `3/6` modules complete
3131

3232
| Order | Module | C++ | C# | Go | Python |
3333
| --- | --- | --- | --- | --- | --- |
3434
| 1 | input-validation | Done | Done | Done | Done |
3535
| 2 | algorithms-basics | Done | Done | Done | Done |
36-
| 3 | file-io-basics | Done | Planned | Planned | Planned |
36+
| 3 | file-io-basics | Done | Done | Done | Done |
3737
| 4 | sorting-and-searching | Done | Planned | Planned | Planned |
3838
| 5 | maps-and-frequency-counting | Done | Planned | Planned | Planned |
3939
| 6 | error-handling-and-defensive-programming | Done | Planned | Planned | Planned |

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ This repository teaches programming through small runnable examples and focused
2525
| Language | Current Levels | Module Coverage | Track Status |
2626
| --- | --- | --- | --- |
2727
| C++ | 00-setup, 01-foundations, 02-core, 03-advanced, 04-expert | Foundations, Core, Advanced, Expert, projects, assessments | Most complete and primary track |
28-
| C# | 01-foundations, 02-core (partial) | 8/8 foundations modules, 2/6 core modules | Foundations complete, core expansion started |
29-
| Go | 01-foundations, 02-core (partial) | 8/8 foundations modules, 2/6 core modules | Foundations complete, core expansion started |
30-
| Python | 01-foundations, 02-core (partial) | 8/8 foundations modules, 2/6 core modules | Foundations complete, core expansion started |
28+
| C# | 01-foundations, 02-core (partial) | 8/8 foundations modules, 3/6 core modules | Foundations complete, core expansion in progress |
29+
| Go | 01-foundations, 02-core (partial) | 8/8 foundations modules, 3/6 core modules | Foundations complete, core expansion in progress |
30+
| Python | 01-foundations, 02-core (partial) | 8/8 foundations modules, 3/6 core modules | Foundations complete, core expansion in progress |
3131

3232
Parity planning reference: [LANGUAGE_PARITY_MATRIX.md](LANGUAGE_PARITY_MATRIX.md)
3333

languages/csharp/02-core/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ This level starts the core track with defensive programming patterns.
66

77
1. [input-validation](./input-validation/README.md)
88
2. [algorithms-basics](./algorithms-basics/README.md)
9+
3. [file-io-basics](./file-io-basics/README.md)
910

1011
Track progress in [../CHECKLIST.md](../CHECKLIST.md).
1112

1213
## Study Tip
1314

14-
Use `algorithms-basics` to practice single-pass algorithms before moving to more complex modules.
15+
Use `file-io-basics` right after `algorithms-basics` to practice parsing real text input safely.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# File I/O Basics (C#)
2+
3+
This module teaches safe text-file reading and writing with basic stream APIs.
4+
5+
## Quick Run
6+
7+
~~~bash
8+
dotnet run --project example/file-io-basics-example.csproj
9+
~~~
10+
11+
## Topics Covered
12+
13+
- Reading text files line by line with `StreamReader`.
14+
- Writing summaries with `StreamWriter`.
15+
- Validating simple `name score` rows with `TryParse`.
16+
- Handling malformed lines without crashing the program.
17+
18+
## Common Pitfalls
19+
20+
- Assuming input files always exist.
21+
- Parsing rows without checking token count first.
22+
- Stopping at the first malformed line instead of skipping it safely.
23+
24+
## Exercise Focus
25+
26+
- exercises/01.cs: copy lines from one file to another with line numbers.
27+
- exercises/02.cs: parse `name score` rows, count invalid rows, and compute average.
28+
29+
### Exercise Specs
30+
31+
1. exercises/01.cs
32+
- Input: source file path and destination file path.
33+
- Output: destination file with numbered lines (`1: ...`, `2: ...`).
34+
- Edge cases: missing source file; empty source file.
35+
36+
2. exercises/02.cs
37+
- Input: file path with rows in the format `name score`.
38+
- Output: valid row count, invalid row count, and average score.
39+
- Edge cases: malformed rows; file with no valid rows.
40+
41+
## Checkpoint
42+
43+
- [ ] I can open files safely and handle missing paths.
44+
- [ ] I can parse and validate simple text rows.
45+
- [ ] I can skip malformed data without aborting the run.
46+
- [ ] I completed exercises/01.cs.
47+
- [ ] I completed exercises/02.cs.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>disable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
</Project>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Example purpose: show the module flow with clear, beginner-friendly steps.
2+
3+
using System;
4+
using System.IO;
5+
6+
class Program
7+
{
8+
static bool TryParseScoreRow(string line, out string name, out int score)
9+
{
10+
name = string.Empty;
11+
score = 0;
12+
13+
// Intent: normalize row parsing so the main flow can stay focused on I/O steps.
14+
string[] parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
15+
// Intent: guard malformed rows before numeric parsing logic runs.
16+
if (parts.Length != 2 || !int.TryParse(parts[1], out score))
17+
{
18+
return false;
19+
}
20+
21+
name = parts[0];
22+
return true;
23+
}
24+
25+
static void Main()
26+
{
27+
// Program flow: prepare input, parse rows, then generate a summary file.
28+
string runDirectory = Path.Combine(Path.GetTempPath(), "learn-lang-file-io-csharp");
29+
Directory.CreateDirectory(runDirectory);
30+
string inputPath = Path.Combine(runDirectory, "scores.txt");
31+
string outputPath = Path.Combine(runDirectory, "summary.txt");
32+
33+
if (!File.Exists(inputPath))
34+
{
35+
File.WriteAllLines(inputPath, new[] { "ana 90", "bob 82", "invalid row", "carla 95" });
36+
}
37+
38+
int validRows = 0;
39+
int sum = 0;
40+
41+
using (StreamReader reader = new StreamReader(inputPath))
42+
{
43+
string? line;
44+
// Intent: iterate through file rows in a deterministic order.
45+
while ((line = reader.ReadLine()) is not null)
46+
{
47+
if (!TryParseScoreRow(line, out string name, out int score))
48+
{
49+
continue;
50+
}
51+
52+
validRows++;
53+
sum += score;
54+
// Intent: print parsed rows so learners can verify intermediate state.
55+
Console.WriteLine($"{name} -> {score}");
56+
}
57+
}
58+
59+
if (validRows == 0)
60+
{
61+
Console.WriteLine("No valid rows found.");
62+
return;
63+
}
64+
65+
double average = (double)sum / validRows;
66+
67+
using (StreamWriter writer = new StreamWriter(outputPath, false))
68+
{
69+
writer.WriteLine($"Rows: {validRows}");
70+
writer.WriteLine($"Average: {average:F2}");
71+
}
72+
73+
Console.WriteLine($"Summary written to {outputPath}");
74+
}
75+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.IO;
3+
4+
class Program
5+
{
6+
static void Main()
7+
{
8+
Console.Write("Input file path: ");
9+
string inputPath = (Console.ReadLine() ?? string.Empty).Trim();
10+
11+
Console.Write("Output file path: ");
12+
string outputPath = (Console.ReadLine() ?? string.Empty).Trim();
13+
14+
if (string.IsNullOrWhiteSpace(inputPath) || string.IsNullOrWhiteSpace(outputPath))
15+
{
16+
Console.WriteLine("Both paths are required.");
17+
return;
18+
}
19+
20+
if (!File.Exists(inputPath))
21+
{
22+
Console.WriteLine("Could not open input file.");
23+
return;
24+
}
25+
26+
try
27+
{
28+
int lineNumber = 1;
29+
using StreamReader reader = new StreamReader(inputPath);
30+
using StreamWriter writer = new StreamWriter(outputPath, false);
31+
32+
string? line;
33+
while ((line = reader.ReadLine()) is not null)
34+
{
35+
writer.WriteLine($"{lineNumber}: {line}");
36+
lineNumber++;
37+
}
38+
39+
Console.WriteLine($"Copied {lineNumber - 1} lines.");
40+
}
41+
catch (IOException)
42+
{
43+
Console.WriteLine("File processing failed.");
44+
}
45+
}
46+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.IO;
3+
4+
class Program
5+
{
6+
static bool TryParseScoreRow(string line, out string name, out int score)
7+
{
8+
name = string.Empty;
9+
score = 0;
10+
11+
string[] parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
12+
if (parts.Length != 2 || !int.TryParse(parts[1], out score))
13+
{
14+
return false;
15+
}
16+
17+
name = parts[0];
18+
return true;
19+
}
20+
21+
static void Main()
22+
{
23+
Console.Write("Enter file path (name score rows): ");
24+
string path = (Console.ReadLine() ?? string.Empty).Trim();
25+
26+
if (!File.Exists(path))
27+
{
28+
Console.WriteLine("Could not open file.");
29+
return;
30+
}
31+
32+
int validRows = 0;
33+
int invalidRows = 0;
34+
int sum = 0;
35+
36+
try
37+
{
38+
using StreamReader reader = new StreamReader(path);
39+
string? line;
40+
while ((line = reader.ReadLine()) is not null)
41+
{
42+
if (TryParseScoreRow(line, out _, out int score))
43+
{
44+
validRows++;
45+
sum += score;
46+
}
47+
else
48+
{
49+
invalidRows++;
50+
}
51+
}
52+
}
53+
catch (IOException)
54+
{
55+
Console.WriteLine("File processing failed.");
56+
return;
57+
}
58+
59+
Console.WriteLine($"Valid rows: {validRows}");
60+
Console.WriteLine($"Invalid rows: {invalidRows}");
61+
62+
if (validRows > 0)
63+
{
64+
double average = (double)sum / validRows;
65+
Console.WriteLine($"Average score: {average:F2}");
66+
}
67+
else
68+
{
69+
Console.WriteLine("No valid rows found.");
70+
}
71+
}
72+
}

languages/csharp/CHECKLIST.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
- [ ] Complete `02-core/input-validation`.
1717
- [ ] Complete `02-core/algorithms-basics`.
18+
- [ ] Complete `02-core/file-io-basics`.
1819

1920
## Parity Goals
2021

languages/csharp/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
This track currently covers `01-foundations` and has started `02-core`.
66

77
- 8/8 foundations modules implemented.
8-
- 2/6 core modules implemented (`input-validation`, `algorithms-basics`).
8+
- 3/6 core modules implemented (`input-validation`, `algorithms-basics`, `file-io-basics`).
99
- Same module naming as C++, Python, and Go for parity.
1010

1111
## Prerequisites
@@ -40,6 +40,7 @@ dotnet run --project 01-foundations/types-and-io/example/types-and-io-example.cs
4040
- [02-core](./02-core/README.md)
4141
- [input-validation](./02-core/input-validation/README.md)
4242
- [algorithms-basics](./02-core/algorithms-basics/README.md)
43+
- [file-io-basics](./02-core/file-io-basics/README.md)
4344

4445
## Progress Tracking
4546

0 commit comments

Comments
 (0)