# Exercise: File I/O

Read a school's grades from a CSV file with mistakes in it, log every line you can't read with your own LogService, write an honor roll, and add new students to the end of a file.

Journeyman · C# · Updated October 30, 2026

A school keeps its students' grades in a CSV file exported from a spreadsheet. Like most real files, it has mistakes in it: a line with a missing column, a GPA of `n/a`, a blank line at the end. You'll write the code that reads it anyway, logs what it can't read, and writes new files from it.

## Get the exercise code

Open this lesson's GitHub repository, [github.com/ask-codey/exercise-file-i-o](https://github.com/ask-codey/exercise-file-i-o), also linked from the **Exercise** box beside the lesson. Click **Code**, then **Download ZIP**, and unzip it into your `bootcamp` folder. Or clone it with Git. Then open `GradeBook.slnx` in Visual Studio.

| File | What's in it |
|---|---|
| `GradeBook/data/student_grades.csv` | The school's file, mistakes and all. It's set to **Copy if newer**, so it's copied next to the program when it builds. |
| `GradeBook/Student.cs`, `IClock.cs`, `SystemClock.cs` | Finished |
| `GradeBook/LogService.cs` | A log file per day, for you to finish |
| `GradeBook/GradeFile.cs` | Reads and writes the CSV files, for you to finish |
| `GradeBook.Tests` | Tests for both. They work in a temporary folder, with a fake clock. |

The requirements are in the comment at the top of each file you finish.

## What to do

| Member | What it does |
|---|---|
| `LogService.CurrentFilePath` | Today's file in the log folder, such as `logs\errors\20260314.log` |
| `LogService.Log(message)` | Appends `2026-03-14 21:05:09 message`, creating the folder and file if needed, and never crashing the app |
| `GradeFile.Read(path)` | The students in the file. Skips the header and blank lines; skips and logs each bad line with its line number; logs a missing file and returns an empty list. |
| `GradeFile.WriteHonorRoll(path, students, minimumGpa)` | A new file with the header and every student at or above the minimum |
| `GradeFile.AddStudent(path, student)` | One line at the end of the file, with the header first if the file is new |

Run the program with **Ctrl+F5**, and the tests with **Test > Run All Tests**.

## Hints

- **Start with `LogService`.** `Read` logs through it, and its tests are the simplest.
- **Dates:** `{clock.Now:yyyyMMdd}` for the file name and `{clock.Now:yyyy-MM-dd HH:mm:ss}` for an entry. Capital `HH` is the 24-hour clock; capital `MM` is the month.
- **Count the lines** in `Read` as you go, starting from 1 for the header, so a logged line number matches what you'd see in an editor.
- **A bad line is expected, not exceptional:** check it with `columns.Length` and `decimal.TryParse`. A missing file is the one thing to `catch`.
- **Replace or append?** `WriteHonorRoll` replaces; `AddStudent` appends. Check `File.Exists` *before* opening the writer, to know whether a new file needs its header.

## When you're done

Every test passes, and the program prints (with today's date):

```text
Read 8 students
firstname,lastname,gpa
Emily,Wilson,3.91
Jane,Garcia,3.96
James,Miller,3.85

Problems logged in logs\errors\20260314.log:
2026-03-14 13:34:28 student_grades.csv line 7: can't read 'David,Jones'
2026-03-14 13:34:28 student_grades.csv line 10: can't read 'Michael,Davis,n/a'
```

Open `student_grades.csv` and check lines 7 and 10: that's what a good log does, it sends you straight to the problem.

## Submit your exercise

The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.

1. On GitHub, click **+**, then **New repository**. Name it (for example `exercise-file-i-o`), choose **Public**, and click **Create repository**. Leave out the README: your folder already has one.
2. In a terminal in your exercise folder (Git Bash on Windows), send your work to your new repo. If you downloaded the ZIP:

   ```bash
   git init -b main
   git add .
   git commit -m "My solution"
   git remote add origin https://github.com/your-name/exercise-file-i-o.git
   git push -u origin main
   ```

   If you cloned the starter instead, point your copy at your own repo, then commit and push:

   ```bash
   git remote set-url origin https://github.com/your-name/exercise-file-i-o.git
   git add .
   git commit -m "My solution"
   git push -u origin main
   ```

3. Paste your repo's address, such as `https://github.com/your-name/exercise-file-i-o`, into **Submit your exercise** on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.

Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.

## So what

Real files are messy. Code that reads the good lines, logs the bad ones with enough detail to fix them, and never crashes over a missing file is code people can trust with their data.

## Key terms

- CSV File
- [StreamReader](https://www.ask-codey.com/glossary/streamreader)
- [Append](https://www.ask-codey.com/glossary/append-c-sharp)
- [Logging](https://www.ask-codey.com/glossary/logging-c-sharp)

## Related

- [Reading a File in C#](https://www.ask-codey.com/lessons/reading-a-file-in-csharp)
- [Appending to a File](https://www.ask-codey.com/lessons/writing-to-files-in-csharp-appending-to-a-file)
- [Creating the LogService](https://www.ask-codey.com/lessons/logging-exceptions-in-csharp-creating-the-logservice)

Source: https://www.ask-codey.com/lessons/exercise-file-i-o
