1. Введение
StreamWriter — один из ключевых классов пространства имён System.IO в .NET, предназначенный для удобной записи текстовых данных в файлы через поток.
Почему нельзя сразу работать с FileStream?
FileStream оперирует исключительно байтами. Если попытаться записать строку через него, придётся самостоятельно преобразовывать текст в байты и заботиться о кодировке (потом вы точно захотите вернуться к StreamWriter).
StreamWriter же сам заботится обо всех этих хлопотах: вы ему отдаёте строку — он пишет нужные байты в файл.
Главные плюсы:
- Легко писать строки, не думая о преобразовании в байты.
- Есть методы для записи текста построчно.
- Управление буферизацией и кодировкой (и мы ещё научимся этим пользоваться).
Простейший пример
using System.IO;
string path = "output.txt";
using (StreamWriter writer = new StreamWriter(path))
{
writer.WriteLine("Привет, мир!");
writer.WriteLine("Это вторая строка.");
}
// После выхода из using-фигурных скобок StreamWriter гарантированно освобождает файл.
Что здесь происходит?
- Открывается файл для записи (если файла нет — он будет создан).
- Каждая строка записывается как отдельная строка в файле (метод WriteLine).
- После блока using файл автоматически закрывается, даже если возникнут ошибки.
Если открыть файл output.txt после выполнения этой программы, вы увидите две строки текста, точно так же, как предполагалось.
Важный нюанс
Если файл уже существует, он будет перезаписан с нуля! Всё, что было внутри — исчезнет. Так что аккуратнее: не храните в таких файлах важную диссертацию или единственный экземпляр квитанции за коммуналку.
2. Запись данных в поток
Главные методы StreamWriter
| Метод | Описание |
|---|---|
|
Записывает строку без перевода строки |
|
Записывает строку с переводом строки |
|
Принудительно записывает буфер в файл (вручную делать редко) |
/ |
Закрывает поток и освобождает ресурс (делает using) |
|
Получить доступ к базовому потоку (например, FileStream) |
Метод Write()
Записывает данные без перехода на новую строку. Всё, что вы напишете далее, окажется в одной и той же строке файла.
Метод WriteLine()
Записывает данные с автоматическим добавлением символа конца строки (\r\n в Windows, \n в Unix-подобных ОС).
Это как нажимать Enter после каждой записи.
Write() vs WriteLine(): демонстрация разницы
using (var writer = new StreamWriter("example.txt"))
{
writer.Write("Первый ");
writer.Write("абзац. ");
writer.WriteLine("Дописали строку, Enter!");
writer.Write("Второй абзац.");
}
Теперь example.txt будет выглядеть примерно так:
Первый абзац. Дописали строку, Enter!
Второй абзац.
3. Работа с кодировками
По умолчанию при создании StreamWriter без указания параметров будет использована кодировка UTF-8 с BOM (Byte Order Mark).
На практике это удобно и современно, но бывают случаи, когда нужно явно задать кодировку — например, для совместимости со старыми программами или импортируемыми данными.
Как указать кодировку?
// Запись файла в кодировке Windows-1251 (кириллица для старых систем)
using (var writer = new StreamWriter("cyrillic.txt", false, System.Text.Encoding.GetEncoding("windows-1251")))
{
writer.WriteLine("Привет, кириллический мир!");
}
Важный момент:
Кодировка должна поддерживаться на системе. Если не уверены — используйте UTF-8.
4. Дополнительные параметры конструктора
Откроем внутреннюю кухню StreamWriter:
public StreamWriter(
string path, // путь к файлу
bool append = false, // дописывать в конец файла?
Encoding encoding = null, // кодировка
int bufferSize = 1024 // размер буфера, байт
)
- append — если false (по умолчанию), файл будет перезаписан. Если true, новые записи добавятся в конец.
- encoding — используемая кодировка.
- bufferSize — размер внутреннего буфера для ускорения работы с большими объемами данных.
Пример: дозапись в тот же файл
// Файл будет дополняться, а не перезаписываться
using (var writer = new StreamWriter("log.txt", append: true))
{
writer.WriteLine(DateTime.Now + " -- Новое событие");
}
5. Полезные нюансы
Что происходит при дозаписи и перезаписи
| Режим | Что делает? | Результат в файле |
|---|---|---|
| append: false | Перезаписывает всё начисто | Старые данные стираются |
| append: true | Добавляет новые строки в конец | Старые строки сохраняются |
Подсказка: Режим дозаписи (append: true) — отличный выбор для логирования, когда нужно сохранять историю событий.
StreamWriter и большие объёмы данных
- StreamWriter буферизует запись: реальные данные попадут в файл чуть позже, чем вы вызвали WriteLine. Но после закрытия потока (или вызова Flush()) всё гарантированно окажется на диске.
- Запись через WriteLine крайне эффективна для построчного вывода. Для сложных форматов (JSON, CSV или XML) лучше использовать соответствующие библиотеки, либо аккуратно экранировать спецсимволы (например, запятые или кавычки).
Как правильно завершать запись и освобождать ресурсы
Правильный способ: всегда использовать using!
Так вы гарантируете, что файл закроется, даже если случится исключение (например, если диск внезапно "кончился" или файл заблокировала другая программа).
flowchart LR
A[Создание StreamWriter] --> B[Работа с файлом]
B --> C{Исключение?}
C -- Да --> D[Dispose вызовется]
C -- Нет --> D
D --> E[Файл гарантированно закрыт]
6. Практические примеры
Пусть в рамках курса у нас есть мини-программа учёта книг. Добавим функциональность записи новых книг в файл.
Пример: Сохраняем новую книгу в отдельный файл
using System;
using System.IO;
class Program
{
static void Main()
{
Console.WriteLine("Введите название книги:");
string bookTitle = Console.ReadLine();
Console.WriteLine("Введите автора:");
string author = Console.ReadLine();
string path = "books.txt";
using (var writer = new StreamWriter(path, append: true))
{
writer.WriteLine($"{bookTitle};{author}");
// Формат CSV: каждая строка - отдельная книга, через точку с запятой
}
Console.WriteLine("Книга сохранена в файле!");
}
}
Попробуйте добавить несколько книг подряд. В файле books.txt строки будут добавляться, не стирая предыдущие. Такое поведение удобно для ведения логов или журналов — например, для вашей будущей системы аудита, когда вы вырастете до Enterprise-разработчика.
7. Как избежать типовых ошибок при записи в файл
Большинство новичков сталкиваются с такими трудностями:
Файл не закрыт и заблокирован другим процессом. Причина — забыли про using.
Записали много строк, но файл остался пустым: забыли вызвать Flush() или закрыть поток (а с using это делается автоматически).
Неожиданно перезаписали файл вместо дозаписи — забыли указать append: true.
Проблемы с кодировкой: файл открывается "кракозябрами" в Блокноте — выбрана не та кодировка или другая программа не поддерживает UTF-8.
Исключения UnauthorizedAccessException или DirectoryNotFoundException: программа пытается сохранить файл туда, где у неё нет прав, или в несуществующую папку. Проверяйте путь и права доступа.
Ошибка "file is used by another process": вы открыли файл на запись, но не закрыли, или параллельно кто-то ещё пытается записать туда же.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ