<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. C#
  3. Working with data
All lessons in this course

Lesson 35 of 60 14 minIntermediate

Files and streams

Read and write text files, build paths safely, process big files line by line with streams, and release files properly with using.

Settings, exports, logs, CSV reports, IFC models: real programs read and write files constantly. The System.IO namespace has everything you need, and is included in new projects by default.

Interactive

Reading and writing a file

A simulated disk with one file path. Try reading before the file exists, then write, append and read again.

C:\Projects\Tower\report.txt
(file does not exist)
File.ReadAllLines(path)Nothing written yet. What happens if you read now?
Path helpers
Path.Combine(folder, file)C:\Projects\Tower\report.txt
Path.GetFileNamereport.txt
Path.GetFileNameWithoutExtensionreport
Path.GetExtension.txt
Path.GetDirectoryNameC:\Projects\Tower

The quick way: File

QuickFile.cs
string path = Path.Combine(AppContext.BaseDirectory, "members.csv"); File.WriteAllText(path, "Mark,Profile,Length\n");          // create or overwriteFile.AppendAllLines(path, new[] { "B1,IPE300,6.0", "C1,HEA200,3.5" }); string[] lines = File.ReadAllLines(path);Console.WriteLine($"{lines.Length} lines");foreach (string line in lines.Skip(1))                      // skip the header{    string[] f = line.Split(',');    Console.WriteLine($"{f[0]} is a {f[1]}");}
Output
3 lines
B1 is a IPE300
C1 is a HEA200
MethodDoes
File.ReadAllText / WriteAllTextwhole file as one string
File.ReadAllLines / WriteAllLineswhole file as a string[] of lines
File.AppendAllText / AppendAllLinesadd to the end (creates the file if needed)
File.ReadLinesreads lazily, one line at a time (for big files)
File.Exists, File.Delete, File.Copy, File.Movemanage files
Directory.CreateDirectory, Directory.GetFileswork with folders

Building paths: always use Path

Paths.cs
string folder = @"C:\Projects\Tower";string file = Path.Combine(folder, "exports", "level2.ifc");   // adds separators correctlyConsole.WriteLine(file);Console.WriteLine(Path.GetExtension(file));Console.WriteLine(Path.GetFileNameWithoutExtension(file));
Output
C:\Projects\Tower\exports\level2.ifc
.ifc
level2

Streams for large files

ReadAllLines loads the whole file into memory. For a 2 GB log or model export that is a problem. A stream reads or writes piece by piece, so memory stays small no matter how big the file is.

Stream.cs
// Write with a StreamWriterusing (var writer = new StreamWriter("big.csv")){    for (int i = 1; i <= 1_000_000; i++)        writer.WriteLine($"B{i},IPE200,{i % 12 + 1}");}   // ← the file is flushed and closed here, even if an exception happened // Read line by line, never holding the whole filelong total = 0;foreach (string line in File.ReadLines("big.csv"))    total += int.Parse(line.Split(',')[2]);Console.WriteLine(total);
Output
6499988

using: always release files

An open file is locked by the operating system. If you forget to close it, other programs (and your own next write) fail with “The process cannot access the file because it is being used by another process”. A using statement closes it automatically at the end of the block, even when an exception is thrown.

UsingDecl.cs
using var reader = new StreamReader("settings.ini");   // C# 8 'using declaration'string? first = reader.ReadLine();// reader is closed automatically at the end of the enclosing method/block

Handling file errors

Errors.cs
try{    string text = File.ReadAllText(path);}catch (FileNotFoundException) { Console.WriteLine("File is missing."); }catch (UnauthorizedAccessException) { Console.WriteLine("No permission."); }catch (IOException ex) { Console.WriteLine($"File problem: {ex.Message}"); }

File.Exists before reading helps, but another program can still delete the file in between, so file code should still handle IOException.

Quick check

What happens to an existing file’s contents when you call File.WriteAllText on it?

Quick check

Why use File.ReadLines instead of File.ReadAllLines for a huge file?

Practice

Write a program that creates a folder "logs" (if needed) and appends a line with the current date/time and the text "Model saved" to "logs/app.log" every time it runs. Then print how many lines the log has.

Show the solution
Solution.cs
string folder = Path.Combine(AppContext.BaseDirectory, "logs");Directory.CreateDirectory(folder);               // does nothing if it already existsstring logPath = Path.Combine(folder, "app.log"); File.AppendAllText(logPath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} Model saved{Environment.NewLine}");Console.WriteLine($"Log has {File.ReadLines(logPath).Count()} line(s)");
Output
Log has 1 line(s)

Remember

  • File methods read or write a whole file in one call.
  • Build paths with Path.Combine, never by string concatenation.
  • Use streams (StreamReader/StreamWriter, File.ReadLines) for large files.
  • Wrap streams in using so files are always closed; handle IOException.
0 of 60 lessons completed