$ sudo teach IT
Модуль 8 · Функции

Урок 8.9 — Работа с файлами

Чтение и запись файлов, работа с путями, StreamReader и StreamWriter.

Почти любая программа работает с файлами: читает конфигурацию, сохраняет результаты, логирует события, обрабатывает данные. C# предоставляет богатый набор классов в пространстве имён System.IO для работы с файловой системой. В этом уроке мы научимся читать, писать, проверять существование файлов и правильно обрабатывать ошибки.

📁

Классы File и FileInfo

В .NET есть два основных класса для работы с файлами: File (статические методы) и FileInfo (экземплярный класс с методами для конкретного файла). Оба находятся в пространстве имён System.IO.

File Статические методы для работы с файлами

Класс File содержит только статические методы. Удобен для быстрых операций, не требует создания экземпляра. Хорош для небольших файлов (читает целиком в память).

C# · Методы File
using System.IO;

// Чтение
string text = File.ReadAllText("file.txt");
string[] lines = File.ReadAllLines("file.txt");
byte[] bytes = File.ReadAllBytes("file.bin");

// Запись
File.WriteAllText("file.txt", "Hello, World!");
File.WriteAllLines("file.txt", new[] { "line1", "line2" });
File.WriteAllBytes("file.bin", new byte[] { 1, 2, 3 });

// Проверка
bool exists = File.Exists("file.txt");
FileInfo info = new FileInfo("file.txt");  // FileInfo — экземплярный класс

// Копирование, перемещение, удаление
File.Copy("source.txt", "dest.txt", overwrite: true);
File.Move("source.txt", "newFolder/source.txt");
File.Delete("obsolete.txt");
FileInfo Экземплярный класс для работы с файлом

FileInfo предоставляет доступ к свойствам файла (размер, дата создания и т.д.) и методы для операций. Полезен, когда нужно многократно обращаться к одному файлу.

C# · FileInfo
var fileInfo = new FileInfo("document.txt");

// Свойства
Console.WriteLine($"Имя: {fileInfo.Name}");
Console.WriteLine($"Полный путь: {fileInfo.FullName}");
Console.WriteLine($"Размер: {fileInfo.Length} байт");
Console.WriteLine($"Расширение: {fileInfo.Extension}");
Console.WriteLine($"Существует: {fileInfo.Exists}");
Console.WriteLine($"Создан: {fileInfo.CreationTime}");
Console.WriteLine($"Изменён: {fileInfo.LastWriteTime}");

// Методы
if (fileInfo.Exists)
{
    fileInfo.CopyTo("backup.txt");
    fileInfo.MoveTo("archive/document.txt");
    fileInfo.Delete();
}

// Свойства Directory
Console.WriteLine($"Папка: {fileInfo.DirectoryName}");
var dir = fileInfo.Directory;
Console.WriteLine($"Файлов в папке: {dir.GetFiles().Length}");
🗂️

Пространство имён System.IO: File, Directory, Path

Пространство имён System.IO содержит основные классы для ввода-вывода. Помимо File и FileInfo, важны Directory (работа с папками) и Path (работа с путями).

Directory — работа с каталогами

C# · Directory
// Создание, удаление, проверка
Directory.CreateDirectory(@"C:\MyApp\Data");
bool dirExists = Directory.Exists(@"C:\MyApp\Data");
Directory.Delete(@"C:\MyApp\Data", recursive: true);

// Получение файлов и подпапок
string[] files = Directory.GetFiles(@"C:\MyApp", "*.txt");
string[] dirs = Directory.GetDirectories(@"C:\MyApp");
string[] all = Directory.GetFileSystemEntries(@"C:\MyApp");

// Текущая директория
string current = Directory.GetCurrentDirectory();

Path — работа с путями

C# · Path
// Комбинирование путей (правильные разделители!)
string fullPath = Path.Combine(@"C:\MyApp", "Data", "file.txt");
// → C:\MyApp\Data\file.txt

string ext = Path.GetExtension("document.txt");  // → .txt
string name = Path.GetFileNameWithoutExtension("document.txt");  // → document
string dir = Path.GetDirectoryName(@"C:\MyApp\Data\file.txt"); // → C:\MyApp\Data
string fileName = Path.GetFileName(@"C:\MyApp\Data\file.txt"); // → file.txt

// Временные файлы
string tempFile = Path.GetTempFileName();
string tempDir = Path.GetTempPath();

// Случайное имя файла
string random = Path.GetRandomFileName();
📖

Чтение файлов: ReadAllText, ReadAllLines, StreamReader

В C# есть несколько способов чтения файлов. Выбор зависит от размера файла и формата данных. File.ReadAllText() и File.ReadAllLines() читают файл целиком — подходят для небольших файлов. StreamReader читает построчно — подходит для больших файлов.

C# · Способы чтения файлов
// Способ 1: File.ReadAllText() — весь файл одной строкой
string content = File.ReadAllText("data.txt");
Console.WriteLine(content);

// Способ 2: File.ReadAllLines() — массив строк
string[] lines = File.ReadAllLines("data.txt");
foreach (string line in lines)
{
    Console.WriteLine($"Строка: {line}");
}

// Способ 3: File.ReadAllLines() + LINQ
var longLines = File.ReadAllLines("data.txt")
    .Where(line => line.Length > 50)
    .ToList();

// Способ 4: StreamReader — чтение построчно (для больших файлов)
using var reader = new StreamReader("bigfile.txt");
string? line;
while ((line = reader.ReadLine()) != null)
{
    Console.WriteLine(line);
}
// Dispose() освободит файл автоматически

// Способ 5: StreamReader — всё сразу
using var reader2 = new StreamReader("data.txt");
string allText = reader2.ReadToEnd();
💡 Для больших файлов (сотни мегабайт) используй StreamReader — он не загружает весь файл в память. File.ReadAllText() читает всё сразу — для файлов больше 100 МБ это может вызвать проблемы с памятью.
✏️

Запись файлов: WriteAllText, WriteAllLines, StreamWriter

Для записи файлов есть симметричные методы. File.WriteAllText() и File.WriteAllLines() — для небольших объёмов. StreamWriter — для построчной записи или больших объёмов.

C# · Способы записи файлов
// Способ 1: File.WriteAllText() — записать строку
File.WriteAllText("output.txt", "Hello, World!");

// Способ 2: File.WriteAllLines() — записать массив строк
string[] data = { "Строка 1", "Строка 2", "Строка 3" };
File.WriteAllLines("output.txt", data);

// Способ 3: File.AppendAllText() — дописать в конец (без перезаписи)
File.AppendAllText("log.txt", $"{DateTime.Now}: действие выполнено\n");

// Способ 4: StreamWriter — построчная запись
using var writer = new StreamWriter("output.txt");
writer.WriteLine("Первая строка");
writer.WriteLine("Вторая строка");
writer.Write("Третья ");
writer.Write("строка (без переноса)");

// Способ 5: StreamWriter с указанием кодировки
using var writer2 = new StreamWriter("utf8.txt", append: true,
    encoding: System.Text.Encoding.UTF8);
writer2.WriteLine("Текст в UTF-8");
💡 По умолчанию File.WriteAllText() использует UTF-8 без BOM. Если нужно дописать в существующий файл, используй File.AppendAllText().
🔒

Блок using для работы с файлами

File.ReadAllText() и File.WriteAllText() автоматически открывают и закрывают файл. Но при использовании StreamReader / StreamWriter нужно явно освобождать ресурсы. Всегда используй using!

C# · Правильная работа с файлами
// ❌ ПЛОХО: файл может не закрыться
static void BadExample()
{
    var reader = new StreamReader("data.txt");
    string data = reader.ReadToEnd();
    // если исключение — файл останется открытым!
}

// ✅ ХОРОШО: using гарантирует закрытие
static void GoodExample()
{
    using var reader = new StreamReader("data.txt");
    string data = reader.ReadToEnd();
    // reader.Dispose() будет вызван при выходе из метода
}

// ✅ Чтение и запись с using
static void CopyFile(string source, string dest)
{
    using var reader = new StreamReader(source);
    using var writer = new StreamWriter(dest);
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        writer.WriteLine(line.ToUpper());
    }
}
🔍

File.Exists(), Path.Combine(), Path.GetExtension()

Три метода, которые ты будешь использовать постоянно при работе с файлами:

File.Exists()

Проверяет, существует ли файл. Возвращает bool. Всегда проверяй перед чтением!

if (File.Exists("config.json"))
{
    string config = File.ReadAllText("config.json");
}
else
{
    Console.WriteLine("Файл не найден!");
}
Path.Combine()

Соединяет части пути с правильным разделителем для ОС. Никогда не склеивай пути через +!

string baseDir = @"C:\Users\Анна\Documents";
string file = Path.Combine(baseDir, "Projects", "data.txt");
// → C:\Users\Анна\Documents\Projects\data.txt
Path.GetExtension()

Извлекает расширение файла (с точкой). Полезно для проверки типа файла.

string file = "report.pdf";
string ext = Path.GetExtension(file);  // → ".pdf"
if (ext == ".txt" || ext == ".csv")
{
    // обрабатываем текстовый файл
}
⚠️

Обработка исключений: FileNotFoundException, IOException

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

C# · Обработка файловых исключений
static string ReadFileSafe(string filePath)
{
    try
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException($"Файл не найден: {filePath}");

        return File.ReadAllText(filePath);
    }
    catch (FileNotFoundException ex)
    {
        Console.WriteLine($"Ошибка: {ex.Message}");
        return string.Empty;
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine("Нет прав на чтение файла");
        return string.Empty;
    }
    catch (IOException ex) when (ex is not FileNotFoundException)
    {
        // IOException — родитель для многих файловых исключений
        Console.WriteLine($"Ошибка ввода-вывода: {ex.Message}");
        return string.Empty;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Неожиданная ошибка: {ex.Message}");
        return string.Empty;
    }
}

Основные файловые исключения:

  • FileNotFoundException — файл не найден.
  • DirectoryNotFoundException — каталог не найден.
  • UnauthorizedAccessException — нет прав доступа.
  • IOException — общая ошибка ввода-вывода (диск полон, файл занят).
  • PathTooLongException — слишком длинный путь.
  • DriveNotFoundException — диск не найден.
❓

Мини-тест

Вопрос 1: Какой класс использовать для чтения больших файлов построчно?

A) File.ReadAllText()
B) StreamReader
C) FileInfo
D) Directory

Правильный ответ: B

Вопрос 2: Как правильно склеить части пути в C#?

A) path1 + "\\" + path2
B) path1 + "/" + path2
C) Path.Combine(path1, path2)
D) path1.ConcatPath(path2)

Правильный ответ: C

Вопрос 3: Какое исключение выбрасывается, если файл не найден?

A) IOException
B) FileNotFoundException
C) NullReferenceException
D) ArgumentException

Правильный ответ: B

Вопрос 4: Как получить расширение файла "image.png"?

A) Path.GetExtension("image.png")
B) File.GetExtension("image.png")
C) Directory.GetExtension("image.png")
D) FileInfo.GetExtension("image.png")

Правильный ответ: A

Вопрос 5: Что вернёт File.Exists() для несуществующего файла?

A) True
B) False
C) null
D) Исключение

Правильный ответ: B

🧪

Задание

Анализатор текстовых файлов

Создай консольную программу для анализа текстовых файлов:

  1. Запроси у пользователя путь к файлу.
  2. Проверь существование файла (File.Exists). Если нет — сообщи об ошибке.
  3. Прочитай файл с помощью StreamReader (с использованием using).
  4. Подсчитай: количество строк, количество слов (разделитель — пробел), количество символов.
  5. Найди самую длинную строку и самый часто встречающийся символ (без учёта регистра).
  6. Запиши результаты анализа в новый файл с помощью StreamWriter.
  7. Добавь обработку исключений (FileNotFoundException, IOException, UnauthorizedAccessException).
  8. Используй Path.Combine для создания пути к файлу результата.

Цель

Отработать чтение, запись, Path.Combine, File.Exists, исключения и using.

Сложность

Средняя

📌

Что важно запомнить

1️⃣
File — статические методы для быстрых операций. FileInfo — экземплярный класс для одного файла.
2️⃣
Чтение: File.ReadAllText() (весь файл строкой), File.ReadAllLines() (массив строк), StreamReader (построчно для больших файлов).
3️⃣
Запись: File.WriteAllText(), File.WriteAllLines(), StreamWriter, File.AppendAllText() для дописывания.
4️⃣
Всегда используй using для StreamReader/StreamWriter. Всегда проверяй File.Exists() и обрабатывай исключения.
5️⃣
Path.Combine() — безопасное склеивание путей. Path.GetExtension() — получение расширения. Directory — работа с папками.

Тест: 8.9: Работа с файлами

3 вопроса

Файлы

Premium