CodeGym /Kursy /C# SELF /Statyczne klasy File

Statyczne klasy File i Directory

C# SELF
Poziom 39 , Lekcja 0
Dostępny

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
File.Exists(path)
Sprawdza, czy plik istnieje pod ścieżką
File.ReadAllText(path)
Odczytuje cały plik jako tekst
File.ReadAllLines(path)
Odczytuje wszystkie linie pliku do tablicy
File.WriteAllText(path, text)
Nadpisuje plik, zapisując tekst
File.AppendAllText(path, text)
Dopisuje tekst na końcu pliku
File.Copy(source, destination)
Kopiuje plik
File.Delete(path)
Usuwa plik
File.Move(source, destination)
Przenosi lub zmienia nazwę pliku
File.Open/Read/Write/Create
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
Directory.Exists(path)
Sprawdza, czy folder istnieje
Directory.CreateDirectory(path)
Tworzy folder (albo ścieżkę ze wszystkimi pośrednimi folderami)
Directory.Delete(path)
Usuwa folder (można z podkatalogami)
Directory.GetFiles(path)
Pobiera listę plików w folderze
Directory.GetDirectories(path)
Pobiera listę podkatalogów
Directory.Move(source, destination)
Przenosi lub zmienia nazwę folderu
Directory.GetCurrentDirectory()
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
File.Exists, Directory.Exists
Odczytać/zapisać cały mały plik
File.ReadAllText, WriteAllText
Dodać tekst na końcu pliku
File.AppendAllText
Pobrać listę plików w katalogu
Directory.GetFiles
Kopiować, usuwać, przenosić plik
File.Copy/Delete/Move
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.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION