1. Wprowadzenie
StreamWriter — to jeden z kluczowych klas przestrzeni nazw System.IO w .NET, przeznaczony do wygodnego zapisywania danych tekstowych do plików przez strumień.
Dlaczego nie można od razu używać FileStream?
FileStream operuje wyłącznie na bajtach. Jeśli spróbujesz zapisać string przez niego, będziesz musiał samodzielnie konwertować tekst na bajty i martwić się o kodowanie (potem na pewno będziesz chciał wrócić do StreamWriter).
StreamWriter sam ogarnia te wszystkie rzeczy: dajesz mu stringa — on zapisuje odpowiednie bajty do pliku.
Główne plusy:
- Łatwo pisać stringi, nie myśląc o konwersji na bajty.
- Są metody do zapisu tekstu linia po linii.
- Możesz kontrolować buforowanie i kodowanie (i jeszcze się tego nauczymy).
Najprostszy przykład
using System.IO;
string path = "output.txt";
using (StreamWriter writer = new StreamWriter(path))
{
writer.WriteLine("Cześć, świecie!");
writer.WriteLine("To jest drugi wiersz.");
}
// Po wyjściu z klamerek using StreamWriter gwarantowanie zwalnia plik.
Co tu się dzieje?
- Otwierany jest plik do zapisu (jeśli pliku nie ma — zostanie utworzony).
- Każda linia jest zapisywana jako osobny wiersz w pliku (metoda WriteLine).
- Po bloku using plik automatycznie się zamyka, nawet jeśli pojawią się błędy.
Jeśli otworzysz plik output.txt po wykonaniu tego programu, zobaczysz dwie linie tekstu, dokładnie tak, jak się spodziewałeś.
Ważny niuans
Jeśli plik już istnieje, zostanie nadpisany od zera! Wszystko, co było w środku — zniknie. Więc uważaj: nie trzymaj w takich plikach ważnej pracy magisterskiej albo jedynego egzemplarza rachunku za prąd.
2. Zapis danych do strumienia
Główne metody StreamWriter
| Metoda | Opis |
|---|---|
|
Zapisuje string bez przejścia do nowej linii |
|
Zapisuje string z przejściem do nowej linii |
|
Wymusza zapis bufora do pliku (ręcznie robi się to rzadko) |
/ |
Zamyka strumień i zwalnia zasób (robi to using) |
|
Dostęp do bazowego strumienia (np. FileStream) |
Metoda Write()
Zapisuje dane bez przechodzenia do nowej linii. Wszystko, co napiszesz dalej, będzie w tej samej linii pliku.
Metoda WriteLine()
Zapisuje dane z automatycznym dodaniem znaku końca linii (\r\n w Windows, \n w systemach Unix).
To jakbyś wciskał Enter po każdym zapisie.
Write() vs WriteLine(): demonstracja różnicy
using (var writer = new StreamWriter("example.txt"))
{
writer.Write("Pierwszy ");
writer.Write("akapit. ");
writer.WriteLine("Dopisaliśmy linię, Enter!");
writer.Write("Drugi akapit.");
}
Teraz example.txt będzie wyglądał mniej więcej tak:
Pierwszy akapit. Dopisaliśmy linię, Enter!
Drugi akapit.
3. Praca z kodowaniem
Domyślnie przy tworzeniu StreamWriter bez podania parametrów zostanie użyte kodowanie UTF-8 z BOM (Byte Order Mark).
W praktyce to wygodne i nowoczesne, ale czasem trzeba jawnie ustawić kodowanie — np. dla zgodności ze starymi programami albo importowanymi danymi.
Jak ustawić kodowanie?
// Zapis pliku w kodowaniu Windows-1251 (cyrylica dla starych systemów)
using (var writer = new StreamWriter("cyrillic.txt", false, System.Text.Encoding.GetEncoding("windows-1251")))
{
writer.WriteLine("Cześć, cyrylicki świecie!");
}
Ważny moment:
Kodowanie musi być obsługiwane przez system. Jeśli nie jesteś pewien — używaj UTF-8.
4. Dodatkowe parametry konstruktora
Zajrzyjmy do środka StreamWriter:
public StreamWriter(
string path, // ścieżka do pliku
bool append = false, // dopisywać na końcu pliku?
Encoding encoding = null, // kodowanie
int bufferSize = 1024 // rozmiar bufora, bajty
)
- append — jeśli false (domyślnie), plik będzie nadpisany. Jeśli true, nowe wpisy zostaną dodane na końcu.
- encoding — używane kodowanie.
- bufferSize — rozmiar wewnętrznego bufora dla przyspieszenia pracy z dużą ilością danych.
Przykład: dopisywanie do tego samego pliku
// Plik będzie uzupełniany, a nie nadpisywany
using (var writer = new StreamWriter("log.txt", append: true))
{
writer.WriteLine(DateTime.Now + " -- Nowe zdarzenie");
}
5. Przydatne niuanse
Co się dzieje przy dopisywaniu i nadpisywaniu
| Tryb | Co robi? | Efekt w pliku |
|---|---|---|
| append: false | Nadpisuje wszystko od nowa | Stare dane są kasowane |
| append: true | Dodaje nowe linie na końcu | Stare linie zostają |
Podpowiedź: Tryb dopisywania (append: true) — świetny wybór do logowania, gdy chcesz zachować historię zdarzeń.
StreamWriter i duże ilości danych
- StreamWriter buforuje zapis: prawdziwe dane trafią do pliku trochę później, niż wywołasz WriteLine. Ale po zamknięciu strumienia (albo wywołaniu Flush()) wszystko na pewno znajdzie się na dysku.
- Zapis przez WriteLine jest bardzo wydajny do wypisywania linia po linii. Do skomplikowanych formatów (JSON, CSV czy XML) lepiej użyć odpowiednich bibliotek, albo ostrożnie uciekać znaki specjalne (np. przecinki czy cudzysłowy).
Jak poprawnie zakończyć zapis i zwolnić zasoby
Poprawny sposób: zawsze używaj using!
Dzięki temu masz pewność, że plik się zamknie, nawet jeśli pojawi się wyjątek (np. jeśli nagle zabraknie miejsca na dysku albo plik zablokuje inny program).
flowchart TD
A[Tworzenie StreamWriter] --> B[Praca z plikiem]
B --> C{Wyjątek?}
C -- Tak --> D[Dispose zostanie wywołany]
C -- Nie --> D
D --> E[Plik gwarantowanie zamknięty]
6. Praktyczne przykłady
Załóżmy, że w ramach kursu mamy mini-program do ewidencji książek. Dodajmy funkcjonalność zapisywania nowych książek do pliku.
Przykład: Zapisujemy nową książkę do osobnego pliku
using System;
using System.IO;
class Program
{
static void Main()
{
Console.WriteLine("Podaj tytuł książki:");
string bookTitle = Console.ReadLine();
Console.WriteLine("Podaj autora:");
string author = Console.ReadLine();
string path = "books.txt";
using (var writer = new StreamWriter(path, append: true))
{
writer.WriteLine($"{bookTitle};{author}");
// Format CSV: każda linia - osobna książka, rozdzielone średnikiem
}
Console.WriteLine("Książka zapisana w pliku!");
}
}
Spróbuj dodać kilka książek pod rząd. W pliku books.txt linie będą się dopisywać, nie kasując poprzednich. Takie zachowanie jest wygodne do prowadzenia logów lub dzienników — np. do twojego przyszłego systemu audytu, gdy już zostaniesz Enterprise-developerem.
7. Jak uniknąć typowych błędów przy zapisie do pliku
Większość początkujących spotyka się z takimi problemami:
Plik nie został zamknięty i jest zablokowany przez inny proces. Powód — zapomniałeś o using.
Zapisałeś dużo linii, ale plik został pusty: zapomniałeś wywołać Flush() albo zamknąć strumień (a z using robi się to automatycznie).
Niechcący nadpisałeś plik zamiast dopisać — zapomniałeś ustawić append: true.
Problemy z kodowaniem: plik otwiera się "krzaczki" w Notatniku — wybrane złe kodowanie albo inny program nie obsługuje UTF-8.
Wyjątki UnauthorizedAccessException albo DirectoryNotFoundException: program próbuje zapisać plik tam, gdzie nie ma uprawnień, albo do nieistniejącego folderu. Sprawdź ścieżkę i uprawnienia.
Błąd "file is used by another process": otworzyłeś plik do zapisu, ale go nie zamknąłeś, albo ktoś inny równolegle próbuje tam pisać.
GO TO FULL VERSION