Files, Directories and Streams
Permissions are enforced by the operating system (UnauthorizedAccessException); attributes such as read-only are exposed through FileInfo.Attributes.
Concept
Files and Directories (System.IO) Static helpers: ReadAllText/Lines/Bytes, WriteAll…, Append…, ReadLines (lazy), Copy, Move, Delete, Exists, async versions
An object for one file: Length, Exists, IsReadOnly, CreationTime, CopyTo, MoveTo Directory / DirectoryInfo
Create, delete, enumerate: CreateDirectory, EnumerateFiles(path, pattern, SearchOption), GetDirectories
Portable path manipulation: Combine, GetFileName, GetExtension, GetDirectoryName, ChangeExtension, GetTempPath, GetRelativePath
Permissions are enforced by the operating system (UnauthorizedAccessException); attributes such as read-only are exposed through FileInfo.Attributes.
A stream is a sequence of bytes you can read, write and (sometimes) seek.
A file (FileMode, FileAccess, FileShare, buffering, async I/O) A byte array in memory Adds a buffer on top of another stream A TCP socket (Module 36) GZipStream, CryptoStream
Decorators that compress or encrypt another stream Properties and methods: CanRead, CanWrite, CanSeek, Position, Length, Seek, Read, Write, CopyTo, Flush.
Readers and writers layer text or typed binary on top of a stream: StreamReader/StreamWriter (text with an encoding) and BinaryReader/BinaryWriter (primitive types, little-endian, length-prefixed strings).
Why It Matters
Test tools read trace files (ASC, BLF, CSV), write reports, archive logs and process binary firmware images. Streaming instead of loading everything keeps memory flat for multi-gigabyte logs.
Syntax
string text = File.ReadAllText(path);
foreach (string line in File.ReadLines(path)) { }
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
using var reader = new StreamReader(fs);
await File.WriteAllTextAsync(path, content);Example
27.1 Files and directories 27.2 Streams, readers and writers
using System.Text;
// Work in a private temp folder so the example is self-contained
string root = Path.Combine(Path.GetTempPath(), "eh-files-demo");
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
DirectoryInfo dir = Directory.CreateDirectory(root);
Directory.CreateDirectory(Path.Combine(root, "logs", "2026"));
// Path: build paths portably - never concatenate with "\" by hand
string logFile = Path.Combine(root, "logs", "2026", "trace.asc");
Console.WriteLine($"file name: {Path.GetFileName(logFile)}, extension: {Path.GetExtension(logFile)}, without ext: {Path.GetFileNameWithoutExtension(logFile)}");
Console.WriteLine($"directory ends with: {Path.GetFileName(Path.GetDirectoryName(logFile))}, changed ext: {Path.GetFileName(Path.ChangeExtension(logFile, ".csv"))}");
// Text files: whole-file helpers
File.WriteAllLines(logFile, ["12.500 18FEF100 01 02", "12.510 0CF00400 FF 00"]);
File.AppendAllText(logFile, "12.520 18FEF100 03 04" + Environment.NewLine);
string[] lines = File.ReadAllLines(logFile);
Console.WriteLine($"lines: {lines.Length}, last: {lines[^1]}");
// Line-by-line streaming for large files
int count = 0;
foreach (string line in File.ReadLines(logF
…using System.Text;
// MemoryStream: a stream over an in-memory buffer
using var memory = new MemoryStream();
using (var writer = new StreamWriter(memory, Encoding.UTF8, leaveOpen: true))
{
writer.WriteLine("line 1");
writer.Write("line 2");
} // disposing the writer flushes it
Console.WriteLine($"MemoryStream length {memory.Length}, position {memory.Position}");
memory.Position = 0; // seek back to the start
using (var reader = new StreamReader(memory, Encoding.UTF8, leaveOpen: true))
Console.WriteLine($"read back: {reader.ReadToEnd().Replace(Environment.NewLine, " / ")}");
// BinaryWriter / BinaryReader: typed binary data (little-endian)
string path = Path.Combine(Path.GetTempPath(), "eh-frame.bin");
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var bw = new BinaryWriter(fs))
{
bw.Write(0x18FEF100u); // uint - 4 bytes
bw.Write((byte)8); // byte - 1 byte
bw.Write(12.6); // double - 8 bytes
bw.Write("VCU"); // length-prefixed string
}
Console.WriteLine($"file
…Expected Result
Files and directories:
(The file length 69 includes Windows line endings \r\n; on Linux/macOS it is 66. The binary file is 17 bytes: 4 + 1 + 8 + 1 length byte + 3 characters.)
file name: trace.asc, extension: .asc, without ext: trace
directory ends with: 2026, changed ext: trace.csv
lines: 3, last: 12.520 18FEF100 03 04
frames with 18FEF100: 2
binary: DEADBEEF
FileInfo: trace.asc 69 bytes, exists True, read-only False
after copy/move/delete: archive.asc
found archive.asc
found logs\2026\trace.asc
async read: PASS: 42 | FAIL: 1
FileNotFoundException: missing.txt
cleaned up: TrueMemoryStream length 17, position 17
read back: line 1 / line 2
file size: 17 bytes
id 0x18FEF100, dlc 8, 12.6 V, ecu VCU, position 17/17
byte at offset 4: 8, CanSeek True, CanWrite False
copied 10000 bytes in 10 chunksCommon Mistakes
Building paths with string concatenation and \ — use Path.Combine.
File.ReadAllText on huge files — stream with File.ReadLines or a StreamReader.
Forgetting to dispose streams (files stay locked) — always using.
Forgetting to flush a writer before reading the underlying stream (disposing the writer flushes it).
Reading Stream.Read once and assuming the buffer is full — it can return fewer bytes; loop, or use ReadExactly.
Assuming a file exists — handle FileNotFoundException/DirectoryNotFoundException.
Best Practices
Use using declarations for every stream/reader/writer.
Prefer async file APIs in servers and UI apps.
Specify encodings explicitly (UTF-8) for text files exchanged with other systems.
Write to a temporary file and then File.Move(..., overwrite: true) to update files atomically.
Interview Relevance
Medium: "File.ReadAllLines vs File.ReadLines", "what is a stream?", "why dispose streams?", "StreamReader vs BinaryReader".
Practice Questions
Count lines, words and characters in a text file (like wc).
Copy a directory tree recursively.
Write 1000 random CAN frames to a binary file and read them back.
Find the 10 largest files under a folder.
Compress a log file with GZipStream and compare sizes.
Quick Quiz
Q1. (Multiple choice) Which method reads a file lazily, line by line? A) File.ReadAllLines B) File.ReadLines C) File.ReadAllText D) File.OpenText().ReadToEnd() Answer: B.
Q2. (True/False) Path.Combine("logs", "2026", "trace.asc") uses the correct separator for the current OS.
Answer: True. Q3. (Predict the output) After writing a uint, a byte, a double and "VCU" with BinaryWriter, how long is the file?
Answer: 17 bytes (verified).
Q4. (Find the bug) A StreamWriter writes to a MemoryStream, and reading the stream right away returns nothing. Why?
Answer: The writer's buffer was not flushed; call Flush() or dispose the writer (with leaveOpen: true) before reading.
Coding Challenge
Write a program that writes three lines to a temp file, then reads it back and prints the line count and the longest line, using File.ReadLines and deleting the file afterwards.
string path = Path.Combine(Path.GetTempPath(), "eh-challenge.txt");
File.WriteAllLines(path, ["short", "a much longer line", "mid line"]);
int count = 0;
string longest = "";
foreach (string line in File.ReadLines(path))
{
count++;
if (line.Length > longest.Length) longest = line;
}
File.Delete(path);
Console.WriteLine($"{count} lines, longest: '{longest}'");