Почему умение работать с файлами в C# — это суперсила?
Представьте, что вы пишете программу, которая должна сохранять настройки пользователя, логировать ошибки или обрабатывать тысячи документов. Без навыков работы с файловой системой вы упрётесь в стену. В C# работа с файлами — это не просто скучное чтение строчек. Это возможность создавать мощные утилиты, парсеры и даже мини-базы данных.
В этой статье мы разберём всё: от банального File.ReadAllText до продвинутой работы с потоками и папками. Вы узнаете, как не потерять данные, как оптимизировать чтение больших файлов и как обходить «подводные камни» файловой системы. Поехали!
Классы-герои: File, FileInfo, Directory и Path
В .NET есть два подхода к работе с файлами: статические методы класса File и экземплярные методы класса FileInfo. Первый — для быстрых операций «прочитал-записал», второй — когда вам нужно работать с одним файлом долго и с сохранением состояния.
File vs FileInfo: когда что использовать?
File — это статический класс. Вы пишете File.Exists("test.txt") — и готово. Удобно, но при каждой операции система проверяет путь заново.
FileInfo — создаёте объект один раз, а потом вызываете методы. Это быстрее, если вы делаете несколько действий с одним файлом.
// Быстрый способ
if (File.Exists(@"C:\data\log.txt"))
{
string content = File.ReadAllText(@"C:\data\log.txt");
Console.WriteLine(content);
}
// Профессиональный способ
FileInfo fileInfo = new FileInfo(@"C:\data\log.txt");
if (fileInfo.Exists)
{
using (StreamReader reader = fileInfo.OpenText())
{
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
}
Класс Directory работает по тому же принципу — статические методы для быстрых операций с папками. Path — незаменимый помощник для склеивания путей: Path.Combine("folder", "file.txt") автоматически ставит правильный слеш.
Чтение файлов: от малого до гигантского
Чтение файлов в C# — это искусство баланса между простотой и производительностью. Давайте разберём три уровня.
Уровень 1: Всё сразу (для маленьких файлов)
Если файл весит до 10 МБ, смело используйте ReadAllText или ReadAllLines.
string text = File.ReadAllText("config.json");
string[] lines = File.ReadAllLines("users.csv");
Минус: при большом файле вы рискуете получить OutOfMemoryException.
Уровень 2: Построчное чтение (для средних файлов)
Используем StreamReader — читаем строку за строкой, не загружая всё в память.
using (StreamReader reader = new StreamReader("big_log.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line); // Обрабатываем каждую строку
}
}
Обратите внимание на using — он гарантирует, что файл закроется, даже если случится ошибка.
Уровень 3: Чтение бинарных данных (для профи)
Работа с изображениями, архивами или любыми не-текстовыми файлами требует FileStream.
using (FileStream fs = new FileStream("photo.jpg", FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
// Обрабатываем кусок данных
Console.WriteLine($"Прочитано {bytesRead} байт");
}
}
Запись файлов: как не потерять данные
Запись в C# — это не только File.WriteAllText. Важно понимать режимы: создать новый файл, дописать в конец или перезаписать.
Основные режимы FileMode
- CreateNew — создаёт новый файл. Если файл существует — выбросит исключение.
- Create — создаёт новый файл или перезаписывает существующий.
- Append — открывает файл и дописывает в конец.
- OpenOrCreate — открывает существующий или создаёт новый.
// Простая запись
File.WriteAllText("note.txt", "Привет, мир!");
// Дозапись в лог
File.AppendAllText("log.txt", $"{DateTime.Now}: Ошибка подключения\n");
// Запись с контролем через StreamWriter
using (StreamWriter writer = new StreamWriter("report.txt", append: true))
{
writer.WriteLine("Новая запись в отчёте");
}
Важно про кодировки
По умолчанию StreamWriter использует UTF-8. Если работаете с кириллицей в старых системах — укажите кодировку явно:
using (StreamWriter writer = new StreamWriter("data.txt", false, Encoding.UTF8))
{
writer.WriteLine("Текст на русском");
}
Работа с файловой системой: папки, перемещение, удаление
Файловая система — это не только файлы, но и папки. C# даёт полный контроль.
Создание и удаление папок
// Создать папку (если её нет)
Directory.CreateDirectory(@"C:\MyApp\Logs");
// Удалить папку со всем содержимым
Directory.Delete(@"C:\Temp", recursive: true);
Будьте осторожны с recursive: true — он удалит всё безвозвратно.
Копирование и перемещение
// Копирование
File.Copy("source.txt", "destination.txt", overwrite: true);
// Перемещение (работает и как переименование)
File.Move(@"C:\old\file.txt", @"C:\new\file.txt");
Совет: перед перемещением всегда проверяйте, что целевой путь не занят, иначе получите исключение.
Обход папок рекурсивно
Хотите найти все файлы .txt в папке и подпапках? Используйте Directory.EnumerateFiles — он не загружает всё в память, а выдаёт результаты по мере обхода.
IEnumerable<string> txtFiles = Directory.EnumerateFiles(
@"C:\Documents",
"*.txt",
SearchOption.AllDirectories
);
foreach (string file in txtFiles)
{
Console.WriteLine($"Найден: {file}");
}
Потоки (Streams): почему это важно понимать
Если вы пишете серьёзное приложение, вам придётся работать с потоками. Stream — это абстракция последовательности байтов. FileStream, MemoryStream, NetworkStream — все они наследники.
Главное правило: любой Stream