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.
System.IO namespace has everything you need, and is included in new projects by default.
The quick way: File
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]}");}3 lines B1 is a IPE300 C1 is a HEA200
| Method | Does |
|---|---|
File.ReadAllText / WriteAllText | |
File.ReadAllLines / WriteAllLines | string[] of lines |
File.AppendAllText / AppendAllLines | |
File.ReadLines | |
File.Exists, File.Delete, File.Copy, File.Move | |
Directory.CreateDirectory, Directory.GetFiles |
Building paths: always use Path
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));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.
// 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);6499988
using: always release files
using statement closes it automatically at the end of the block, even when an exception is thrown.
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/blockHandling file errors
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.
Show the solution
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)");Log has 1 line(s)
Remember
Filemethods 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 usingso files are always closed; handleIOException.