1. Wprowadzenie
Praca bezpośrednio ze strumieniami (StreamReader, StreamWriter, FileStream) jest bardzo przydatna, zwłaszcza gdy trzeba kontrolować proces czytania/zapisu na niskim poziomie: np. zapisywać bloki "po trochu" lub przetwarzać duże pliki częściami.
Ale w praktyce często trzeba po prostu sprawdzić: "czy ten plik istnieje?", "skopiować plik z miejsca na miejsce", "usunąć plik", "dostac listę wszystkich .txt w folderze" i tak dalej. Do takich zadań Microsoft stworzył dwa uniwersalne narzędzia: statyczne klasy File i Directory. Statyczne — znaczy, nie trzeba tworzyć instancji, od razu wywołujesz metodę po nazwie klasy.
Analogia z codziennego życia
Jeśli strumienie — to ty z ołówkiem, który starannie pisze każdą literę na kartce, to File i Directory — to twój stół z szufladami: możesz "natychmiast" otworzyć szufladę (Directory), wyjąć z niej karteczkę (File.ReadAllText), wyrzucić karteczkę (File.Delete) albo nawet wyjąć całą szufladę i wysłać ją na drugi koniec pokoju (Directory.Move). Proste zadania — proste metody.
2. Klasa File: co to jest i do czego służy
Klasa File ma wygodny zestaw statycznych metod do wszystkich popularnych operacji na plikach.
Najczęściej używane metody
| Metoda | Opis |
|---|---|
|
Sprawdza, czy plik istnieje pod ścieżką |
|
Odczytuje cały plik jako tekst |
|
Odczytuje wszystkie linie pliku do tablicy |
|
Nadpisuje plik, zapisując tekst |
|
Dopisuje tekst na końcu pliku |
|
Kopiuje plik |
|
Usuwa plik |
|
Przenosi lub zmienia nazwę pliku |
|
Otwiera strumień do bardziej zaawansowanych operacji |
Szybki przykład: sprawdzamy plik i czytamy go
string filePath = "data.txt";
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine("Zawartość pliku:");
Console.WriteLine(content);
}
else
{
Console.WriteLine("Plik nie znaleziony!");
}
Widzisz? Żadnego ręcznego zarządzania strumieniami w prostym przypadku!
3. Klasa Directory: zarządzaj folderami bez bólu
Jeśli File — o pojedynczych plikach, to Directory — o folderach (katalogach).
Podstawowe zadania
| Metoda | Opis |
|---|---|
|
Sprawdza, czy folder istnieje |
|
Tworzy folder (albo ścieżkę ze wszystkimi pośrednimi folderami) |
|
Usuwa folder (można z podkatalogami) |
|
Pobiera listę plików w folderze |
|
Pobiera listę podkatalogów |
|
Przenosi lub zmienia nazwę folderu |
|
Pobiera bieżący katalog roboczy aplikacji |
Przykład: tworzymy folder i zapisujemy tam plik
string dirPath = "Results";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
Console.WriteLine("Folder 'Results' utworzony.");
}
string filePath = Path.Combine(dirPath, "summary.txt");
File.WriteAllText(filePath, "Dane końcowe: ...");
Console.WriteLine($"Plik {filePath} zapisany.");
4. Typowe scenariusze użycia File i Directory
Podstawowe metody wydają się nudne, jeśli nie widzieć ich w akcji! Zobaczmy, jak można użyć tych klas do rozwiązywania realnych zadań i, przy okazji, rozwinąć nasze przykładowe aplikacje (wyobraźmy sobie, że robimy prosty system do prowadzenia listy zadań — todo-list, wynik zapisujemy w plikach).
Zapis danych do pliku (zamiana starego pliku)
// Zapisujemy listę zadań do pliku
string[] todos = { "Kupić chleb", "Zadzwonić do lekarza", "Zrobić zadanie domowe z C#" };
string filePath = "todo.txt";
File.WriteAllLines(filePath, todos);
Console.WriteLine("Lista zadań zapisana.");
Ładowanie danych z pliku
// Ładujemy zadania, jeśli plik istnieje
if (File.Exists(filePath))
{
string[] loadedTodos = File.ReadAllLines(filePath);
Console.WriteLine("Twoja lista zadań:");
foreach (var task in loadedTodos)
{
Console.WriteLine("- " + task);
}
}
else
{
Console.WriteLine("Na razie nie masz listy zadań.");
}
Dodanie nowego zadania (bez nadpisywania pliku)
string newTask = "Wyprowadzić psa";
File.AppendAllText(filePath, newTask + Environment.NewLine);
Console.WriteLine("Zadanie dodane!");
Kopiowanie i tworzenie kopii zapasowej pliku
string backupPath = "todo_backup.txt";
File.Copy(filePath, backupPath, overwrite: true);
Console.WriteLine("Kopia zapasowa listy zadań utworzona.");
Przenoszenie (lub zmiana nazwy) pliku
string archivePath = "todo_archive.txt";
File.Move(filePath, archivePath);
Console.WriteLine("Lista zadań zarchiwizowana (zmieniono nazwę).");
Usuwanie pliku
if (File.Exists(archivePath))
{
File.Delete(archivePath);
Console.WriteLine("Archiwum usunięte — zwalniamy miejsce!");
}
5. Praca z folderami: przykłady
Tworzenie hierarchii katalogów
string path = Path.Combine("Reports", "2024", "June");
Directory.CreateDirectory(path);
Console.WriteLine($"Folder {path} utworzony (łącznie z katalogami pośrednimi).");
Pobieranie listy plików i folderów
string dirPath = "Reports";
if (Directory.Exists(dirPath))
{
string[] files = Directory.GetFiles(dirPath);
Console.WriteLine("Pliki w folderze:");
foreach (var file in files)
{
Console.WriteLine(file);
}
string[] subDirs = Directory.GetDirectories(dirPath);
Console.WriteLine("Podkatalogi:");
foreach (var dir in subDirs)
{
Console.WriteLine(dir);
}
}
Filtrowanie plików po masce (tylko txt)
string[] txtFiles = Directory.GetFiles(dirPath, "*.txt");
Console.WriteLine("Tylko .txt pliki:");
foreach (var file in txtFiles)
{
Console.WriteLine(file);
}
Rekurencyjne przeszukiwanie (pliki we wszystkich podkatalogach)
string[] allFiles = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine("Pliki we wszystkich zagnieżdżonych folderach:");
foreach (var file in allFiles)
{
Console.WriteLine(file);
}
Przenoszenie i usuwanie katalogów
string from = "OldReports";
string to = "Archive/OldReports";
if (Directory.Exists(from))
{
Directory.Move(from, to);
Console.WriteLine($"Folder {from} przeniesiony do {to}");
}
if (Directory.Exists(to))
{
Directory.Delete(to, recursive: true); // true: usuń wszystko wewnątrz
Console.WriteLine($"Folder {to} usunięty (z całą zawartością).");
}
6. Przydatne niuanse
Integracja ze ścieżkami: klasa Path
Przy pracy z plikami i folderami często trzeba składać pełne ścieżki, łączyć nazwy katalogów i plików, rozbierać rozszerzenia, sprawdzać dozwolone znaki. Do tego służy pomocnicza klasa Path.
Przykłady
string folder = "Results";
string filename = "week1.txt";
string fullPath = Path.Combine(folder, filename); // bezpieczne łączenie!
Console.WriteLine(fullPath); // "Results/week1.txt" (albo "\" na Windows)
- Pobrać rozszerzenie pliku: Path.GetExtension(fullPath)
- Pobrać nazwę pliku bez ścieżki: Path.GetFileName(fullPath)
Używaj Path.Combine zamiast konkatenacji stringów — to bezpieczniejsze dla aplikacji cross-platformowych.
Metody "wszystko naraz" kontra wariant strumieniowy
Wiele metod File i Directory (np. File.ReadAllText, File.WriteAllText, Directory.GetFiles) robi całą pracę "w całości": odczytują/zapisują/pobierają listę plików w jednym kroku. To BARDZO wygodne, ale nie nadaje się do bardzo dużych plików lub katalogów, gdzie może zabraknąć pamięci. Jeśli pracujesz z gigabajtami — przejdź na klasy strumieniowe (StreamReader, StreamWriter, FileStream itd.) albo przetwarzanie plików partiami.
Kiedy używać File/Directory, a kiedy — klas strumieni
| Sytuacja | Co użyć |
|---|---|
| Trzeba sprawdzić, czy plik/folder istnieje | |
| Odczytać/zapisać cały mały plik | |
| Dodać tekst na końcu pliku | |
| Pobrać listę plików w katalogu | |
| Kopiować, usuwać, przenosić plik | |
| Kopiować/usunąć cały folder | Directory.Delete/Move/Copy (Copy — z biblioteką zewnętrzną) |
| Przetwarzać BARDZO duże pliki partiami | Klas strumieni (StreamReader, FileStream) |
Praktyczne znaczenie i rzeczywiste zastosowania
- Szybkie ładowanie/zapisywanie konfiguracji, ustawień, plików JSON/XML.
- Systemy logowania (zapis logów na dysk).
- Proste narzędzia do backupu i migracji danych.
- Skany folderów z multimediami, zdjęciami, dokumentami.
- Scenariusze użytkownika: ładowanie szablonów, eksport/import, generowanie raportów itp.
Praktyczne zadania na rozmowach kwalifikacyjnych też często wymagają użycia właśnie tych klas: "Zaimplementuj funkcję, która policzy sumaryczny rozmiar wszystkich .txt plików w katalogu", "Zrób kopię zapasową plików do osobnego folderu" itd.
Wizualna ściągawka: kiedy której metody użyć
graph TD
A[Co chcesz zrobić?]
A -->|Sprawdzić istnienie| B[File.Exists lub Directory.Exists]
A -->|Odczytać/zapisać plik w całości| C[File.ReadAllText/WriteAllText]
A -->|Dodać dane do pliku| D[File.AppendAllText]
A -->|Praca z folderami| E[Directory.CreateDirectory, GetFiles, GetDirectories]
A -->|Kopiować/usuń/przenieść plik| F[File.Copy/Delete/Move]
A -->|Kopiować/usuń folder w całości| G[Directory.Delete/Move]
7. Cechy i typowe błędy
Sprawdzanie istnienia i "race condition". Nawet jeśli sprawdzisz, że plik/folder istnieje, między tą kontrolą a rzeczywistą operacją inny proces może to zmienić. Dlatego zawsze używaj try-catch nawet jeśli wcześniej sprawdziłeś obecność!
Prawa dostępu. Jeśli aplikacja nie ma wystarczających uprawnień do odczytu/zapisu/usunięcia, metody rzucą UnauthorizedAccessException.
Ścieżka za długa. Maksymalna długość ścieżki (zwykle 260 znaków na starszych Windows; w .NET 9 ograniczenia są łagodniejsze, ale i tak uważaj).
Łapanie błędów. Metody typu File.ReadAllText lub Directory.GetFiles nie przebaczają błędów: jeśli plik/folder nie istnieje — od razu wyjątek. Owiń w try-catch albo wcześniej sprawdź istnienie.
GO TO FULL VERSION