1. Введение
Работать напрямую с потоками (StreamReader, StreamWriter, FileStream) очень полезно, особенно когда нужно контролировать процесс чтения/записи на низком уровне: например, писать блоки «по чуть-чуть» или обрабатывать большие файлы частями.
Но в реальной жизни часто нужно просто узнать: «существует ли этот файл?», «скопировать файл с места на место», «удалить файл», «получить список всех .txt-файлов в папке», и так далее. Для подобных задач Microsoft создала два универсальных инструмента: статические классы File и Directory. Статические — значит, создавать экземпляр не нужно, сразу вызываете нужный метод по имени класса.
Жизненная аналогия
Если потоки — это вы с карандашом, который аккуратно пишет каждую букву на листе бумаги, то File и Directory — это ваш стол с ящиками: вы можете «моментально» открыть ящик (Directory), достать из него бумажку (File.ReadAllText), выкинуть бумажку (File.Delete) или даже вытащить целый ящик и отправить его в другой конец комнаты (Directory.Move). Простые задачи — простые методы.
2. Класс File: что это и для чего нужен
У класса File есть очень удобный набор статических методов для всех популярных операций с файлами.
Самые часто используемые методы
| Метод | Описание |
|---|---|
|
Проверяет, есть ли файл по пути |
|
Читает весь файл как текст |
|
Читает все строки файла в массив |
|
Перезаписывает файл, записывая текст |
|
Дописывает текст в конец файла |
|
Копирует файл |
|
Удаляет файл |
|
Перемещает или переименовывает файл |
|
Открывает поток для сложных операций |
Быстрый пример: проверяем файл и читаем его
string filePath = "data.txt";
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine("Содержимое файла:");
Console.WriteLine(content);
}
else
{
Console.WriteLine("Файл не найден!");
}
Видите? Никакого ручного управления потоками для простого случая!
3. Класс Directory: управляй папками без боли
Если File — про отдельные файлы, то Directory — про папки (директории).
Основные задачи
| Метод | Описание |
|---|---|
|
Проверяет, есть ли папка |
|
Создаёт папку (или путь со всеми промежуточными папками) |
|
Удаляет папку (можно с поддиректориями) |
|
Получает список файлов в папке |
|
Получает список поддиректорий |
|
Перемещает или переименовывает папку |
|
Получить текущую рабочую директорию приложения |
Пример: создаём папку и сохраняем туда файл
string dirPath = "Results";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
Console.WriteLine("Папка 'Results' создана.");
}
string filePath = Path.Combine(dirPath, "summary.txt");
File.WriteAllText(filePath, "Итоговые данные: ...");
Console.WriteLine($"Файл {filePath} записан.");
4. Типичные сценарии использования File и Directory
Базовые методы покажутся скучными, если не видеть их в деле! Посмотрим, как можно использовать эти классы для решения реальных задач и, заодно, продолжим развитие нашего условного приложения (давайте представим, что мы делаем простую систему для ведения списка дел — todo-list, результат сохраняем в файлы).
Сохранение данных в файл (замена старого файла)
// Сохраняем список задач в файл
string[] todos = { "Купить хлеб", "Позвонить врачу", "Сделать домашку по C#" };
string filePath = "todo.txt";
File.WriteAllLines(filePath, todos);
Console.WriteLine("Список дел сохранён.");
Загрузка данных из файла
// Загружаем задачи, если файл есть
if (File.Exists(filePath))
{
string[] loadedTodos = File.ReadAllLines(filePath);
Console.WriteLine("Ваш список дел:");
foreach (var task in loadedTodos)
{
Console.WriteLine("- " + task);
}
}
else
{
Console.WriteLine("У вас пока нет списка дел.");
}
Добавление новой задачи (без перезаписи файла)
string newTask = "Погулять с собакой";
File.AppendAllText(filePath, newTask + Environment.NewLine);
Console.WriteLine("Задача добавлена!");
Копирование и резервное копирование файла
string backupPath = "todo_backup.txt";
File.Copy(filePath, backupPath, overwrite: true);
Console.WriteLine("Резервная копия списка дел создана.");
Перемещение (или переименование) файла
string archivePath = "todo_archive.txt";
File.Move(filePath, archivePath);
Console.WriteLine("Список дел архивирован (переименован).");
Удаление файла
if (File.Exists(archivePath))
{
File.Delete(archivePath);
Console.WriteLine("Архив удалён — освобождаем место!");
}
5. Работа с папками: примеры
Создание иерархии директорий
string path = Path.Combine("Reports", "2024", "June");
Directory.CreateDirectory(path);
Console.WriteLine($"Папка {path} создана (включая промежуточные папки).");
Получение списка файлов и папок
string dirPath = "Reports";
if (Directory.Exists(dirPath))
{
string[] files = Directory.GetFiles(dirPath);
Console.WriteLine("Файлы в папке:");
foreach (var file in files)
{
Console.WriteLine(file);
}
string[] subDirs = Directory.GetDirectories(dirPath);
Console.WriteLine("Поддиректории:");
foreach (var dir in subDirs)
{
Console.WriteLine(dir);
}
}
Фильтрация файлов по маске (только txt)
string[] txtFiles = Directory.GetFiles(dirPath, "*.txt");
Console.WriteLine("Только .txt-файлы:");
foreach (var file in txtFiles)
{
Console.WriteLine(file);
}
Рекурсивный обход (файлы во всех подкаталогах)
string[] allFiles = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine("Файлы во всех вложенных папках:");
foreach (var file in allFiles)
{
Console.WriteLine(file);
}
Перемещение и удаление директорий
string from = "OldReports";
string to = "Archive/OldReports";
if (Directory.Exists(from))
{
Directory.Move(from, to);
Console.WriteLine($"Папка {from} перемещена в {to}");
}
if (Directory.Exists(to))
{
Directory.Delete(to, recursive: true); // true: удалить всё внутри
Console.WriteLine($"Папка {to} удалена (со всем содержимым).");
}
6. Полезные нюансы
Интеграция с путями: класс Path
При работе с файлами и папками часто приходится составлять полные пути, объединять имена директорий и файлов, разбирать расширения, проверять допустимость символов. Для этого используют вспомогательный класс Path.
Примеры
string folder = "Results";
string filename = "week1.txt";
string fullPath = Path.Combine(folder, filename); // безопасное объединение!
Console.WriteLine(fullPath); // "Results/week1.txt" (или "\" на Windows)
- Получить расширение файла: Path.GetExtension(fullPath)
- Получить имя файла без пути: Path.GetFileName(fullPath)
Используйте Path.Combine вместо конкатенации строк — это безопаснее для кроссплатформенных приложений.
Методы «всё и сразу» против потокового варианта
Многие методы File и Directory (например, File.ReadAllText, File.WriteAllText, Directory.GetFiles) делают всю работу «целиком»: считывают/записывают/получают список файлов за одну операцию. Это ОЧЕНЬ удобно, но не подходит для очень больших файлов или каталогов, где может закончиться память. Если работаете с гигабайтами — переходите на потоковые классы (StreamReader, StreamWriter, FileStream и т.д.) или обработку файлов по частям.
Когда использовать File/Directory, а когда — классы потоков
| Ситуация | Что использовать |
|---|---|
| Нужно проверить, есть ли файл/папка | |
| Прочитать/записать весь небольшой файл | |
| Добавить текст в конец файла | |
| Получить список файлов в директории | |
| Копировать, удалить, переместить файл | |
| Копировать/удалить целую папку | Directory.Delete/Move/Copy (Copy — со сторонней библиотекой) |
| Обрабатывать ОЧЕНЬ большие файлы по частям | Классы потоков (StreamReader, FileStream) |
Практическое значение и реальное применение
- Быстрая загрузка/сохранение конфигураций, настроек, JSON/XML-файлов.
- Системы логирования (запись логов на диск).
- Простые утилиты резервного копирования (бэкапы) и миграции данных.
- Сканирование папок с мультимедиа, фотографиями, документами.
- Пользовательские сценарии: загрузка шаблонов, экспорт/импорт, генерация отчётов и пр.
Практические задачи на собеседованиях тоже часто требуют использовать именно эти классы: «Реализуйте функцию, которая считает суммарный размер всех .txt файлов в директории», «Сделайте резервную копию файлов в отдельную папку» и т.п.
Визуальная шпаргалка: когда какой метод использовать
graph TD
A[Что вы хотите сделать?]
A -->|Проверить существование| B[File.Exists или Directory.Exists]
A -->|Прочитать/записать файл целиком| C[File.ReadAllText/WriteAllText]
A -->|Добавить данные в файл| D[File.AppendAllText]
A -->|Работа с папками| E[Directory.CreateDirectory, GetFiles, GetDirectories]
A -->|Копировать/удалить/переместить файл| F[File.Copy/Delete/Move]
A -->|Копировать/удалить папку целиком| G[Directory.Delete/Move]
7. Особенности и типичные ошибки
Проверка на существование и «состояние гонки». Даже если проверить, что файл/папка существует, между этой проверкой и реальной операцией другой процесс может всё изменить. Поэтому всегда используйте try-catch даже если уже проверили наличие!
Права доступа. Если приложению не хватает прав на чтение/запись/удаление, методы выбросят UnauthorizedAccessException.
Путь слишком длинный. Максимальная длина пути (обычно 260 символов на старых Windows; в .NET 9 ограничения мягче, но всё равно остерегайтесь).
Ловля ошибок. Методы вроде File.ReadAllText или Directory.GetFiles не прощают ошибок: если файла/папки нет — сразу исключение. Оберните в try-catch либо предварительно проверьте наличие.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ