9_Работа с файловой системой

Урок 9 : Работа с файловой системой

О чём этот урок

Файлы позволяют сохранять данные после завершения программы. В этом уроке мы научимся работать с путями и каталогами, выполнять основные операции с файлами, а также читать и записывать обычный текст.


1. Файлы, каталоги и пути

Файл - именованная область на диске, в которой хранятся данные. Например: player.txt, settings.ini, log.txt.

Каталог или папка - контейнер для файлов и других каталогов.

Путь указывает расположение файла или каталога в файловой системе.

Data/player.txt
C:\Games\MyGame\Data\player.txt

Пути бывают двух видов:

Текущую рабочую директорию можно получить через Directory.GetCurrentDirectory():

using System.IO;

string currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
Рабочая директория может меняться

Относительный путь считается не от файла с кодом, а от текущей рабочей директории запущенного приложения. Она может отличаться при запуске программы из IDE и из собранного приложения.


2. Построение путей с помощью Path

Пути не следует собирать обычной конкатенацией строк:

// Ненадёжный вариант
string path = Directory.GetCurrentDirectory() + "\\Data\\player.txt";

Класс Path строит путь с корректными для операционной системы разделителями:

string dataDirectory = Path.Combine(
    Directory.GetCurrentDirectory(),
    "Data"
);

string filePath = Path.Combine(dataDirectory, "player.txt");

Console.WriteLine(dataDirectory);
Console.WriteLine(filePath);

Полезные методы Path

string path = Path.Combine("Data", "Saves", "player.txt");

Console.WriteLine(Path.GetFileName(path));                 // player.txt
Console.WriteLine(Path.GetFileNameWithoutExtension(path)); // player
Console.WriteLine(Path.GetExtension(path));                // .txt
Console.WriteLine(Path.GetDirectoryName(path));            // Data\Saves
Console.WriteLine(Path.IsPathRooted(path));                 // false
Метод Назначение
Path.Combine() Объединяет части пути
Path.GetFileName() Возвращает имя файла
Path.GetFileNameWithoutExtension() Возвращает имя без расширения
Path.GetExtension() Возвращает расширение файла
Path.GetDirectoryName() Возвращает путь к каталогу
Path.IsPathRooted() Проверяет, является ли путь абсолютным

3. Работа с каталогами

Класс Directory содержит статические методы для работы с каталогами.

Проверка и создание каталога

string dataDirectory = Path.Combine(
    Directory.GetCurrentDirectory(),
    "Data"
);

if (!Directory.Exists(dataDirectory))
{
    Directory.CreateDirectory(dataDirectory);
    Console.WriteLine("Каталог Data создан");
}

Directory.CreateDirectory() безопасно вызывать, даже если каталог уже существует:

Directory.CreateDirectory(dataDirectory);

Если каталога нет, он будет создан. Если каталог существует, ошибка не возникнет.

Получение списка файлов

string[] files = Directory.GetFiles(dataDirectory);

foreach (string file in files)
{
    Console.WriteLine(Path.GetFileName(file));
}

Можно искать только файлы с определённым расширением:

string[] textFiles = Directory.GetFiles(dataDirectory, "*.txt");

Другие операции

string[] directories = Directory.GetDirectories(dataDirectory);

Directory.Move("OldData", "NewData");

// Второй аргумент true разрешает удалить непустой каталог
Directory.Delete("TemporaryData", true);
Удаление каталогов

Directory.Delete(path, true) удаляет каталог вместе со всем содержимым. Перед вызовом необходимо внимательно проверить путь.


4. Работа с файлами

Класс File предоставляет статические методы для создания, чтения, копирования, перемещения и удаления файлов.

string sourcePath = Path.Combine("Data", "player.txt");
string copyPath = Path.Combine("Data", "player_copy.txt");

if (File.Exists(sourcePath))
{
    File.Copy(sourcePath, copyPath, overwrite: true);
    Console.WriteLine("Файл скопирован");
}

Основные операции:

bool exists = File.Exists(sourcePath);

File.Copy(sourcePath, copyPath, overwrite: true);
File.Move(copyPath, Path.Combine("Data", "player_old.txt"));
File.Delete(sourcePath);
Метод Назначение
File.Exists() Проверяет существование файла
File.Copy() Копирует файл
File.Move() Перемещает или переименовывает файл
File.Delete() Удаляет файл

File.Delete() не выбрасывает исключение, если файла нет, но может завершиться ошибкой, если нет прав доступа или файл используется другой программой.


5. Чтение и запись TXT

TXT - обычный текстовый файл. В нём нет обязательной структуры: программа сама определяет, что означает каждая строка.

Запись текста

string dataDirectory = Path.Combine(
    Directory.GetCurrentDirectory(),
    "Data"
);
Directory.CreateDirectory(dataDirectory);

string filePath = Path.Combine(dataDirectory, "player.txt");
string text = "Имя: Alex\nУровень: 5\nОчки: 1250";

File.WriteAllText(filePath, text);

File.WriteAllText() создаёт файл, если его нет. Если файл уже существует, старое содержимое будет заменено.

Чтение всего файла

if (File.Exists(filePath))
{
    string textFromFile = File.ReadAllText(filePath);
    Console.WriteLine(textFromFile);
}
else
{
    Console.WriteLine("Файл не найден");
}

Запись и чтение набора строк

string[] lines =
{
    "Имя: Alex",
    "Уровень: 5",
    "Очки: 1250"
};

File.WriteAllLines(filePath, lines);

string[] loadedLines = File.ReadAllLines(filePath);

foreach (string line in loadedLines)
{
    Console.WriteLine(line);
}

Добавление текста

string notesPath = Path.Combine(dataDirectory, "notes.txt");
File.AppendAllText(notesPath, "Новая строка\n");

В отличие от WriteAllText, метод AppendAllText не стирает существующие данные, а добавляет текст в конец.

Метод Результат
File.WriteAllText() Записывает строку, заменяя старое содержимое
File.ReadAllText() Читает весь файл в одну строку
File.WriteAllLines() Записывает коллекцию строк
File.ReadAllLines() Читает файл в массив строк
File.AppendAllText() Добавляет текст в конец файла

6. StreamWriter и StreamReader

Методы File.ReadAllText() и File.WriteAllText() удобны для небольших файлов. Они загружают или записывают содержимое целиком.

Для последовательной работы с данными используются потоки:

Запись через StreamWriter

string filePath = Path.Combine("Data", "player.txt");

using (StreamWriter writer = new StreamWriter(filePath))
{
    writer.WriteLine("Имя: Alex");
    writer.WriteLine("Уровень: 5");
    writer.WriteLine("Очки: 1250");
}

Чтение через StreamReader

using (StreamReader reader = new StreamReader(filePath))
{
    while (!reader.EndOfStream)
    {
        string line = reader.ReadLine();
        Console.WriteLine(line);
    }
}

Конструкция using гарантирует закрытие файла после завершения блока, даже если во время работы возникла ошибка.

Современная сокращённая запись:

using StreamWriter writer = new StreamWriter(filePath);

writer.WriteLine("Первая строка");
writer.WriteLine("Вторая строка");
Когда использовать потоки

Потоки полезны для больших файлов и последовательной обработки данных. Для небольших текстовых файлов методы класса File обычно проще.


7. Информация о файле

Класс FileInfo представляет информацию о конкретном файле:

FileInfo info = new FileInfo(filePath);

Console.WriteLine($"Имя: {info.Name}");
Console.WriteLine($"Расширение: {info.Extension}");
Console.WriteLine($"Размер: {info.Length} байт");
Console.WriteLine($"Создан: {info.CreationTime}");
Console.WriteLine($"Изменён: {info.LastWriteTime}");

Получение информации обо всех файлах каталога:

foreach (string file in Directory.GetFiles(dataDirectory))
{
    FileInfo info = new FileInfo(file);
    Console.WriteLine($"{info.Name} | {info.Length} байт");
}

8. Возможные ошибки

Частые причины ошибок при работе с файловой системой:

Перед чтением необходимо проверять существование файла:

if (!File.Exists(filePath))
{
    Console.WriteLine("Файл не найден");
    return;
}

string text = File.ReadAllText(filePath);

Проверка File.Exists() не защищает от всех возможных проблем. Обработка исключений будет подробно рассмотрена в отдельном уроке.

На этом этапе важно:

  1. Создавать каталог перед записью.
  2. Проверять наличие файла перед чтением.
  3. Закрывать потоки с помощью using.
  4. Не удалять и не перезаписывать файлы без необходимости.

Итог урока


📋 Перейти к практическим заданиям урока 9

Материалы для дополнительного ознакомления :

Файлы и каталоги
Работа с классом File
FileStream
StreamReader и StreamWriter