Paweł Łukasiewicz: programista blogger
Paweł Łukasiewicz
2026-04-01
Paweł Łukasiewicz: programista blogger
Paweł Łukasiewicz
2026-04-01
Udostępnij Udostępnij Kontakt
Wprowadzenie

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!

📅 Timeline - ewolucja File I/O
  • .NET 1.0 (2002) - File, Directory, StreamReader/Writer
  • .NET 4.0 (2010) - Memory-mapped files
  • .NET 4.5 (2012) - Async file operations
  • .NET Core 2.1 (2018) - 🔥 System.IO.Pipelines - high performance!
  • .NET 6+ (2021+) - Performance improvements, Span-based APIs
File, Directory, Path - podstawy

File - operacje na plikach

// File.ReadAllText - cały plik do stringa
string content = File.ReadAllText("data.txt");
Console.WriteLine(content);

// File.WriteAllText - zapisz string do pliku
File.WriteAllText("output.txt", "Hello World!");

// File.ReadAllLines - każda linia jako element array
string[] lines = File.ReadAllLines("data.txt");
foreach (var line in lines)
{
    Console.WriteLine(line);
}

// File.WriteAllLines - zapisz array linii
string[] newLines = { "Line 1", "Line 2", "Line 3" };
File.WriteAllLines("output.txt", newLines);

// File.AppendAllText - dopisz do pliku (nie nadpisuj)
File.AppendAllText("log.txt", $"{DateTime.Now}: Log entry\n");

// File.ReadAllBytes - binary data
byte[] bytes = File.ReadAllBytes("image.png");

// File.WriteAllBytes - zapisz binary
File.WriteAllBytes("copy.png", bytes);

// File.Exists - sprawdź czy plik istnieje
if (File.Exists("data.txt"))
{
    Console.WriteLine("Plik istnieje!");
}

// File.Delete - usuń plik
File.Delete("temp.txt");

// File.Copy - kopiuj plik
File.Copy("source.txt", "destination.txt", overwrite: true);

// File.Move - przenieś/zmień nazwę
File.Move("old.txt", "new.txt");

Directory - operacje na folderach

// Directory.Exists - sprawdź czy folder istnieje
if (Directory.Exists("C:\\Temp"))
{
    Console.WriteLine("Folder istnieje!");
}

// Directory.CreateDirectory - stwórz folder
Directory.CreateDirectory("C:\\Temp\\NewFolder");

// Directory.Delete - usuń folder
Directory.Delete("C:\\Temp\\OldFolder");  // Pusty folder
Directory.Delete("C:\\Temp\\OldFolder", recursive: true);  // Z zawartością

// Directory.GetFiles - wszystkie pliki w folderze
string[] files = Directory.GetFiles("C:\\Temp");
foreach (var file in files)
{
    Console.WriteLine(file);
}

// Directory.GetFiles - z pattern
string[] txtFiles = Directory.GetFiles("C:\\Temp", "*.txt");
string[] allTxtFiles = Directory.GetFiles("C:\\Temp", "*.txt", SearchOption.AllDirectories);

// Directory.GetDirectories - wszystkie podfoldery
string[] folders = Directory.GetDirectories("C:\\Temp");

// Directory.EnumerateFiles - lazy enumeration (lepsze dla dużych folderów)
foreach (var file in Directory.EnumerateFiles("C:\\Temp", "*.txt"))
{
    Console.WriteLine(file);
    // Nie ładuje wszystkich naraz - jedna po drugiej
}

Path - operacje na ścieżkach

// Path.Combine - łącz ścieżki (cross-platform!)
string path = Path.Combine("C:\\Temp", "subfolder", "file.txt");
// Windows: C:\Temp\subfolder\file.txt
// Linux: C:/Temp/subfolder/file.txt

// Path.GetFileName - nazwa pliku
string fileName = Path.GetFileName("C:\\Temp\\data.txt");  // "data.txt"

// Path.GetFileNameWithoutExtension - bez rozszerzenia
string name = Path.GetFileNameWithoutExtension("data.txt");  // "data"

// Path.GetExtension - rozszerzenie
string ext = Path.GetExtension("data.txt");  // ".txt"

// Path.GetDirectoryName - folder
string dir = Path.GetDirectoryName("C:\\Temp\\data.txt");  // "C:\Temp"

// Path.GetFullPath - pełna ścieżka
string fullPath = Path.GetFullPath("data.txt");  // Relatywna → Absolutna

// Path.GetTempPath - folder temp
string tempPath = Path.GetTempPath();

// Path.GetTempFileName - unikalny plik temp
string tempFile = Path.GetTempFileName();

// Path.ChangeExtension - zmień rozszerzenie
string newPath = Path.ChangeExtension("data.txt", ".json");  // "data.json"
StreamReader/StreamWriter - streaming I/O

StreamReader - czytanie dużych plików

// 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 File Operations - I/O bez blokowania

Async File methods

// Async file operations - nie blokują thread!

// ReadAllTextAsync
string content = await File.ReadAllTextAsync("data.txt");

// WriteAllTextAsync
await File.WriteAllTextAsync("output.txt", "Hello Async!");

// ReadAllLinesAsync
string[] lines = await File.ReadAllLinesAsync("data.txt");

// WriteAllLinesAsync
await File.WriteAllLinesAsync("output.txt", new[] { "Line 1", "Line 2" });

// AppendAllTextAsync
await File.AppendAllTextAsync("log.txt", $"{DateTime.Now}: Entry\n");

// ReadAllBytesAsync / WriteAllBytesAsync
byte[] bytes = await File.ReadAllBytesAsync("image.png");
await File.WriteAllBytesAsync("copy.png", bytes);

// Dlaczego async?
// - Nie blokuje thread podczas I/O
// - W ASP.NET: może obsłużyć więcej requestów
// - W UI: aplikacja nie freezuje

StreamReader/Writer Async

// 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
  • 🔥 System.IO.Pipelines - high-performance, zero-copy!

Następny wpis: JSON i serializacja C#!