16. File I/O
Read and write text files with File, StreamReader, and StreamWriter; manipulate the filesystem with Path and Directory. Pair with using to release resources safely.
What you'll learn
- 1Use `File.ReadAllText`/`WriteAllText`/`ReadAllLines`/`WriteAllLines`
- 2Read line-by-line with `StreamReader`
- 3Manipulate OS-independent paths with the `Path` class
- 4Create safe temp files with `Path.GetTempFileName()`
- 5Get a taste of `async` file I/O
Overview
Almost every app reads config files, writes logs, or parses CSV. C# offers a rich `System.IO` namespace. Use `File` static methods for short tasks, and the `Stream` family for large files.
Core Concepts
1) `File` static methods (small files)
Simplest way to read/write a file in one go.
File.WriteAllText("a.txt", "Hi"); // write whole string
string text = File.ReadAllText("a.txt"); // read entire content
File.WriteAllLines("b.txt", new[] {"a","b"}); // write line-by-line
string[] lines = File.ReadAllLines("b.txt"); // read line-by-line (array)Above tens of MB this eats memory; use streams instead.
2) `StreamReader` (large files)
Reading one line at a time keeps memory low. It implements `IDisposable`, so wrap with `using`.
using var sr = new StreamReader(path);
string? line;
while ((line = sr.ReadLine()) is not null)
{
// process line
}3) `Path` β path manipulation
Don't manually concatenate with `"/"` or `"\\"` β use `Path.Combine`. The separator differs by OS.
string p = Path.Combine("logs", "2025", "app.log");
string name = Path.GetFileName(p); // "app.log"
string ext = Path.GetExtension(p); // ".log"
string tmp = Path.GetTempFileName(); // empty file in OS temp dir`Path.GetTempFileName()` makes a non-colliding file automatically. Safe for learning examples.
4) Pair with exception handling
File ops can almost always fail (missing file, no permission, etc.).
try
{
string s = File.ReadAllText(path);
}
catch (FileNotFoundException) { /* ... */ }
catch (UnauthorizedAccessException) { /* ... */ }
catch (IOException) { /* other I/O errors */ }5) Async I/O (`async`/`await`)
Disks are slow. In UI and server code, do I/O asynchronously so other work isn't blocked. Lecture 19 covers this in depth, but the form is simple β method name ends in `Async` and you `await` the result.
string s = await File.ReadAllTextAsync(path);
await File.WriteAllTextAsync(path, s);> Detailed `async`/`await` study is in **lecture 19**. Just learn the shape here.
Examples
Example 1 β `WriteRead`: write and read full text
string path = Path.GetTempFileName(); // temp file
File.WriteAllText(path, "Hello, file!\nSecond line");
string text = File.ReadAllText(path);
Console.WriteLine("== Read content ==");
Console.WriteLine(text);
File.Delete(path); // cleanup**Output**
== Read content ==
Hello, file!
Second line**Note:** `Path.GetTempFileName()` creates a non-colliding file in the OS temp folder.
Example 2 β `WriteReadLines`: handle as a line array
string path = Path.GetTempFileName();
string[] names = ["Alice", "Bob", "Charlie"];
File.WriteAllLines(path, names);
string[] loaded = File.ReadAllLines(path);
foreach (var n in loaded)
Console.WriteLine($"- {n}");
File.Delete(path);**Output**
- Alice
- Bob
- Charlie**Note:** For line-oriented data, `WriteAllLines`/`ReadAllLines` are cleanest.
Example 3 β `StreamReaderUse`: stream line-by-line
string path = Path.GetTempFileName();
File.WriteAllLines(path, ["apple", "banana", "cherry"]);
// Use a using block to scope sr's lifetime explicitly.
using (var sr = new StreamReader(path))
{
int lineNo = 1;
string? line;
while ((line = sr.ReadLine()) is not null)
{
Console.WriteLine($"{lineNo}: {line}");
lineNo++;
}
}
File.Delete(path);**Output**
1: apple
2: banana
3: cherry**Note:** Memory stays flat even for big files. Leaving the `using` block immediately closes the handle so the following `File.Delete` won't hit a Windows file lock (`using var` would hold the handle to end-of-method and conflict).
Example 4 β `PathHelpers`: split and join paths
string combined = Path.Combine("logs", "2025", "app.log");
Console.WriteLine($"Combine : {combined}");
Console.WriteLine($"FileName : {Path.GetFileName(combined)}");
Console.WriteLine($"Extension : {Path.GetExtension(combined)}");
Console.WriteLine($"NoExt : {Path.GetFileNameWithoutExtension(combined)}");
string temp = Path.GetTempFileName();
Console.WriteLine($"Temp : {temp}");
File.Delete(temp);**Output**
Combine : logs/2025/app.log
FileName : app.log
Extension : .log
NoExt : app
Temp : /tmp/tmpXXXXXX.tmp**Note:** On Windows you'd see `\`. **No manual concatenation β use `Path.Combine`**.
Example 5 β `AsyncFile`: async read/write
// top-level await: available from C# 9 / .NET 5+
string path = Path.GetTempFileName();
await File.WriteAllTextAsync(path, "Stored async!");
string text = await File.ReadAllTextAsync(path);
Console.WriteLine($"Read: {text}");
File.Delete(path);**Output**
Read: Stored async!**Note:** Same behavior as the sync version, but other work can run while the disk is busy. More in lecture 19.
Full example code (src/)
src/AsyncFile/AsyncFile.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/AsyncFile/Program.cs
// async file I/O (top-level await β C# 9 / .NET 5+)
// Detailed async/await is in lecture 19
string path = Path.GetTempFileName();
await File.WriteAllTextAsync(path, "Stored async!");
string text = await File.ReadAllTextAsync(path);
Console.WriteLine($"Read: {text}");
File.Delete(path);
src/PathHelpers/PathHelpers.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/PathHelpers/Program.cs
// Path utility methods
string combined = Path.Combine("logs", "2025", "app.log");
Console.WriteLine($"Combine : {combined}");
Console.WriteLine($"FileName : {Path.GetFileName(combined)}");
Console.WriteLine($"Extension : {Path.GetExtension(combined)}");
Console.WriteLine($"NoExt : {Path.GetFileNameWithoutExtension(combined)}");
string temp = Path.GetTempFileName();
Console.WriteLine($"Temp : {temp}");
File.Delete(temp);
src/StreamReaderUse/Program.cs
// StreamReader: streams line-by-line (large-file friendly)
string path = Path.GetTempFileName();
File.WriteAllLines(path, ["apple", "banana", "cherry"]);
// Scope sr explicitly with a using block β Dispose on block exit.
// With `using var`, the handle would live to end-of-method and on Windows the
// File.Delete below would hit a file lock.
using (var sr = new StreamReader(path))
{
int lineNo = 1;
string? line;
while ((line = sr.ReadLine()) is not null)
{
Console.WriteLine($"{lineNo}: {line}");
lineNo++;
}
}
// sr is disposed by now, so delete is safe.
File.Delete(path);
src/StreamReaderUse/StreamReaderUse.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/WriteRead/Program.cs
// Write full text to a temp file and read it back
string path = Path.GetTempFileName();
File.WriteAllText(path, "Hello, file!\nSecond line");
string text = File.ReadAllText(path);
Console.WriteLine("== Read content ==");
Console.WriteLine(text);
File.Delete(path); // leave no traces in the working dir
src/WriteRead/WriteRead.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/WriteReadLines/Program.cs
// Write and read line-by-line in one shot
string path = Path.GetTempFileName();
string[] names = ["Alice", "Bob", "Charlie"];
File.WriteAllLines(path, names);
string[] loaded = File.ReadAllLines(path);
foreach (var n in loaded)
{
Console.WriteLine($"- {n}");
}
File.Delete(path);
src/WriteReadLines/WriteReadLines.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Common Mistakes
- Hand-concatenating with `"a" + "/" + "b"` β OS-dependent. Use `Path.Combine`.
- Reading a huge file with `ReadAllText` β memory blow-up. Stream line by line.
- Using `StreamReader` without `using` β file-handle leak.
- Dropping temp files in the working dir β pollutes the environment. Use **`Path.GetTempFileName()`**.
- Skipping exception handling β prepare for missing files, permissions, full disks, etc.
Summary
- Small text: `File.ReadAllText`/`WriteAllText`
- Line-oriented: `File.ReadAllLines`/`WriteAllLines`
- Big files: `StreamReader` + `using`
- Paths: `Path.Combine`; temp files: `Path.GetTempFileName()`
- On slow disks, use `*Async` + `await` (preview of lecture 19)
Practice
**Practice - 16. File I/O**
Problem 1 β Number-prefixed output
- Project folder: `Homework01/`
- Key concepts: `Path.GetTempFileName`, `File.WriteAllLines`, `File.ReadAllLines`
Requirements
- Create a temp file with `Path.GetTempFileName()`.
- Write `["Seoul", "Busan", "Daegu", "Gwangju"]` line-by-line.
- Read it back and print as `[1] Seoul` etc.
- Delete the file before exit.
Expected output
[1] Seoul
[2] Busan
[3] Daegu
[4] GwangjuHints
- Use `File.WriteAllLines(path, lines)` and `File.ReadAllLines(path)`.
- Index with `for (int i = 0; i < lines.Length; i++)` or an `Enumerate` pattern.
Problem 2 β CSV column sum
- Project folder: `Homework02/`
- Key concepts: line/field parsing, numeric conversion, accumulation
Requirements
- Write a CSV like this (header + 3 rows) to a temp file:
``` name,score Alice,80 Bob,95 Charlie,72 ```
- Read line-by-line, skipping the header, and sum the second column (`score`).
- Print sum and average (integer division is fine).
Expected output
Sum: 247
Average: 82Hints
- Split columns with `string.Split(',')`.
- Convert with `int.Parse(parts[1])`.
- Skip the header with `continue;` when `i == 0`.
Check your answer
Try it yourself, then compare against the [`answer/`](./answer/) folder.
Answer (answer/)
homework/answer/Homework01/Homework01.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
homework/answer/Homework01/Program.cs
// Write to a temp file, then print with numbers
string path = Path.GetTempFileName();
string[] cities = ["Seoul", "Busan", "Daegu", "Gwangju"];
File.WriteAllLines(path, cities);
string[] loaded = File.ReadAllLines(path);
for (int i = 0; i < loaded.Length; i++)
{
Console.WriteLine($"[{i + 1}] {loaded[i]}");
}
File.Delete(path);
homework/answer/Homework02/Homework02.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.IoEx16</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
homework/answer/Homework02/Program.cs
// Build a temp CSV file and compute sum/average of the score column
string path = Path.GetTempFileName();
string[] csv =
[
"name,score",
"Alice,80",
"Bob,95",
"Charlie,72",
];
File.WriteAllLines(path, csv);
string[] lines = File.ReadAllLines(path);
int total = 0;
int count = 0;
for (int i = 0; i < lines.Length; i++)
{
if (i == 0) continue; // skip header
string[] parts = lines[i].Split(',');
total += int.Parse(parts[1]);
count++;
}
Console.WriteLine($"Sum: {total}");
Console.WriteLine($"Average: {total / count}");
File.Delete(path);
Try It Yourself
cd src/WriteRead && dotnet run
cd ../WriteReadLines && dotnet run
cd ../StreamReaderUse && dotnet run
cd ../PathHelpers && dotnet run
cd ../AsyncFile && dotnet runNext Lecture
[17_String_Processing](../17_%EB%AC%B8%EC%9E%90%EC%97%B4_%EC%B2%98%EB%A6%AC/) β Tools to manipulate the text you read in.
All lecture materials and example code are openly available on GitHub.
View on GitHub β