Praca z plikami to fundamentalna umiejętność każdego programisty! W 2015 roku używałeś File.ReadAllText i StreamReader. W 2026 roku masz async file operations, memory-mapped files dla ogromnych plików, i System.IO.Pipelines dla high-performance I/O!
// StreamReader - czyta plik linia po linii (nie całość do pamięci!)
using (var reader = new StreamReader("large-file.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
// Przetwarza jedną linię - mało pamięci! ✨
}
}
// C# 8+ - using declaration (czytelniejsze)
using var reader2 = new StreamReader("large-file.txt");
while (!reader2.EndOfStream)
{
string line = reader2.ReadLine();
ProcessLine(line);
}
// ReadToEnd - cały plik (jak File.ReadAllText, ale przez stream)
using var reader3 = new StreamReader("file.txt");
string content = reader3.ReadToEnd();
// Encoding - określ encoding
using var reader4 = new StreamReader("file.txt", Encoding.UTF8);
// Przykład: Przetwarzanie dużego CSV
using var reader5 = new StreamReader("data.csv");
reader5.ReadLine(); // Skip header
while (!reader5.EndOfStream)
{
var line = reader5.ReadLine();
var values = line.Split(',');
ProcessCsvRow(values);
}
StreamWriter - zapisywanie dużych plików
// StreamWriter - zapisuj plik linia po linii
using (var writer = new StreamWriter("output.txt"))
{
writer.WriteLine("First line");
writer.WriteLine("Second line");
writer.WriteLine("Third line");
}
// Append mode - dopisuj do istniejącego pliku
using var writer2 = new StreamWriter("log.txt", append: true);
writer2.WriteLine($"{DateTime.Now}: Log entry");
// Encoding
using var writer3 = new StreamWriter("output.txt", append: false, Encoding.UTF8);
// Przykład: Generowanie dużego CSV
using var writer4 = new StreamWriter("export.csv");
writer4.WriteLine("Id,Name,Email"); // Header
for (int i = 0; i < 1_000_000; i++)
{
writer4.WriteLine($"{i},User{i},user{i}@example.com");
// Nie trzyma wszystkiego w pamięci - pisze na bieżąco! ✨
}
// AutoFlush - wymusza zapis do dysku po każdym Write
using var writer5 = new StreamWriter("critical.log");
writer5.AutoFlush = true; // Ważne dla logów - natychmiastowy zapis
FileStream - low-level control
// FileStream - niskopoziomowa kontrola
using var stream = new FileStream("data.bin", FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ProcessBytes(buffer, bytesRead);
}
// FileMode options:
// - CreateNew: Nowy plik (error jeśli istnieje)
// - Create: Nowy plik (nadpisz jeśli istnieje)
// - Open: Otwórz istniejący (error jeśli nie istnieje)
// - OpenOrCreate: Otwórz lub stwórz
// - Truncate: Otwórz i wyczyść
// - Append: Otwórz i dopisuj na końcu
// FileAccess: Read, Write, ReadWrite
// Przykład: Zapisz binary data
using var outStream = new FileStream("output.bin", FileMode.Create);
byte[] data = new byte[] { 1, 2, 3, 4, 5 };
outStream.Write(data, 0, data.Length);
// Async StreamReader
await using var reader = new StreamReader("large-file.txt");
while (!reader.EndOfStream)
{
string line = await reader.ReadLineAsync(); // Async!
await ProcessLineAsync(line);
}
// Async StreamWriter
await using var writer = new StreamWriter("output.txt");
for (int i = 0; i < 1000; i++)
{
await writer.WriteLineAsync($"Line {i}"); // Async!
}
// Przykład: Async CSV processing
public async Task ProcessCsvAsync(string filePath)
{
await using var reader = new StreamReader(filePath);
await reader.ReadLineAsync(); // Skip header
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
var values = line.Split(',');
await ProcessCsvRowAsync(values); // Async processing
}
}
// FileStream async
await using var stream = new FileStream("data.bin", FileMode.Open);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await ProcessBytesAsync(buffer, bytesRead);
}
Async performance - przykład
// Sync - blokuje thread
public void DownloadFiles(List<string> urls)
{
foreach (var url in urls)
{
var content = httpClient.GetStringAsync(url).Result; // BLOCKS!
File.WriteAllText($"file_{urls.IndexOf(url)}.html", content); // BLOCKS!
}
}
// 100 plików * 1s = 100 sekund! Thread zablokowany całe 100s! 😱
// Async - nie blokuje thread
public async Task DownloadFilesAsync(List<string> urls)
{
var tasks = urls.Select(async (url, index) =>
{
var content = await httpClient.GetStringAsync(url); // Non-blocking!
await File.WriteAllTextAsync($"file_{index}.html", content); // Non-blocking!
});
await Task.WhenAll(tasks);
}
// 100 plików concurrently - ~1-2 sekundy! Thread zwolniony podczas I/O! ✨
Memory-mapped Files - ogromne pliki
Problem z dużymi plikami
// Problem: Plik 10GB
byte[] data = File.ReadAllBytes("huge-file.bin"); // ❌ OutOfMemoryException!
// Nie możesz załadować 10GB do pamięci!
// StreamReader/FileStream - działa, ale wolne dla random access
using var stream = new FileStream("huge-file.bin", FileMode.Open);
stream.Seek(1_000_000_000, SeekOrigin.Begin); // Seek do 1GB
// Wolne dla wielu seeks!
Memory-mapped files - rozwiązanie!
🎉 .NET 4.0 - Memory-mapped Files
Mapuj plik bezpośrednio do pamięci - OS zarządza ładowaniem! Szybki random access do ogromnych plików!
// Memory-mapped file - mapuj plik do pamięci
using var mmf = MemoryMappedFile.CreateFromFile("huge-file.bin", FileMode.Open);
// Stwórz view (accessor) do części pliku
using var accessor = mmf.CreateViewAccessor(offset: 1_000_000_000, size: 1024);
// Czytaj dane jakby były w pamięci!
byte value = accessor.ReadByte(0); // Fast random access!
int intValue = accessor.ReadInt32(4);
// Zapisz dane
accessor.Write(0, (byte)42);
accessor.Write(4, 12345);
// OS zarządza ładowaniem tylko używanych fragmentów - super wydajne! ✨
// Przykład: Przeszukaj ogromny plik
public long FindPattern(string filePath, byte[] pattern)
{
using var mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open);
using var accessor = mmf.CreateViewAccessor();
long fileSize = new FileInfo(filePath).Length;
for (long i = 0; i < fileSize - pattern.Length; i++)
{
bool match = true;
for (int j = 0; j < pattern.Length; j++)
{
if (accessor.ReadByte(i + j) != pattern[j])
{
match = false;
break;
}
}
if (match)
return i; // Znaleziono!
}
return -1; // Nie znaleziono
}
Inter-process communication z MMF
// Memory-mapped files - IPC (komunikacja między procesami)
// Process 1 - tworzy shared memory
using var mmf = MemoryMappedFile.CreateNew("SharedMemory", 1024);
using var accessor = mmf.CreateViewAccessor();
// Zapisz dane
accessor.Write(0, "Hello from Process 1");
// Process 2 - czyta shared memory
using var mmf2 = MemoryMappedFile.OpenExisting("SharedMemory");
using var accessor2 = mmf2.CreateViewAccessor();
// Czytaj dane
byte[] buffer = new byte[100];
accessor2.ReadArray(0, buffer, 0, buffer.Length);
string message = Encoding.UTF8.GetString(buffer);
Console.WriteLine(message); // "Hello from Process 1"
// Oba procesy mają dostęp do tej samej pamięci! ✨
System.IO.Pipelines - high-performance I/O
Problem z tradycyjnym I/O
// Problem: Tradycyjne network I/O
public async Task ProcessNetworkDataAsync(NetworkStream stream)
{
var buffer = new byte[4096];
while (true)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
// Problem 1: Alokacja buffer dla każdego read
// Problem 2: Kopiowanie danych
// Problem 3: Trudne parsowanie (co jeśli wiadomość jest w 2 bufferach?)
ProcessData(buffer, bytesRead);
}
}
System.IO.Pipelines - rozwiązanie!
🎉 .NET Core 2.1 - System.IO.Pipelines
High-performance I/O! Zero-copy, buffer pooling, backpressure! Używane w Kestrel (ASP.NET Core server)!
// Pipelines - producer/consumer pattern
var pipe = new Pipe();
// Producer - pisze dane do pipe
Task producerTask = FillPipeAsync(pipe.Writer);
// Consumer - czyta dane z pipe
Task consumerTask = ReadPipeAsync(pipe.Reader);
await Task.WhenAll(producerTask, consumerTask);
async Task FillPipeAsync(PipeWriter writer)
{
while (true)
{
// Get buffer from pool - no allocation! ✨
Memory<byte> memory = writer.GetMemory(4096);
// Read data into buffer
int bytesRead = await stream.ReadAsync(memory);
if (bytesRead == 0) break;
// Tell writer how much was written
writer.Advance(bytesRead);
// Flush to reader
FlushResult result = await writer.FlushAsync();
if (result.IsCompleted) break;
}
await writer.CompleteAsync();
}
async Task ReadPipeAsync(PipeReader reader)
{
while (true)
{
// Read from pipe
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;
// Process data
ProcessData(buffer);
// Tell reader how much was consumed
reader.AdvanceTo(buffer.End);
if (result.IsCompleted) break;
}
await reader.CompleteAsync();
}
Pipelines - parsing protocol
// Przykład: Parse HTTP-like protocol (linia po linii)
async Task ProcessLinesAsync(PipeReader reader)
{
while (true)
{
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;
// Szukaj \r\n (koniec linii)
while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))
{
ProcessLine(line);
}
// Tell reader how much was consumed
reader.AdvanceTo(buffer.Start, buffer.End);
if (result.IsCompleted) break;
}
await reader.CompleteAsync();
}
bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
{
// Szukaj \r\n
var position = buffer.PositionOf((byte)'\n');
if (position == null)
{
line = default;
return false;
}
// Wytnij linię
line = buffer.Slice(0, position.Value);
// Przesuń buffer za \r\n
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
return true;
}
Dlaczego Pipelines?
// Zalety System.IO.Pipelines:
// 1. Buffer pooling - zero allocations
// Memory<byte> memory = writer.GetMemory(); // From pool! ✨
// 2. Backpressure - automatyczna kontrola flow
// if (result.IsCompleted) break; // Reader nie nadąża? Stop writing!
// 3. Zero-copy - dane nie są kopiowane
// ReadOnlySequence<byte> - może być w wielu segmentach, no copy!
// 4. Parsowanie uproszczone
// buffer.PositionOf((byte)'\n'); // Built-in helpers
// Performance comparison:
// Traditional I/O: 1000 req/s, 500MB allocations/s
// System.IO.Pipelines: 10000 req/s, 50MB allocations/s ✨
// Używane w:
// - Kestrel (ASP.NET Core web server)
// - SignalR
// - gRPC
// - Wszystkie high-performance scenarios!
Podsumowanie
✅ File/Directory/Path - podstawowe operacje
✅ StreamReader/Writer - streaming I/O, małe zużycie pamięci
✅ Async operations - non-blocking I/O
✅ Memory-mapped files - ogromne pliki, fast random access