Запись в файл с C#
В этом посте представлен обзор некоторых доступных альтернатив записи текста в файл в C#. Решение должно создать новый файл, если он не существует, и обрезать существующий файл перед записью.
1. Использование File Class
Класс File предоставляет несколько статических служебных методов для записи текста в файл. Ниже приведены примеры каждого из этих методов:
⮚ File.WriteAllText() метод
⮚ File.WriteAllLines() метод
⮚ File.WriteAllBytes() метод
Обратите внимание, что вышеупомянутые методы перезаписывают существующий файл. Чтобы добавить текст в файл, используйте любой из AppendAllLines , AppendAllText , или же AppendText() методы.
2. Использование StreamWriter Class
StreamWriter class предоставляет несколько методов для записи в файл. Мы можем использовать его Write() метод записи строки в поток.
В следующем примере показано, как использовать StreamWriter объект для записи данных в новый файл. Dispose() метод вызывается автоматически для сброса и закрытия потока, поскольку средство записи потока объявляется и создается в операторе using.
Чтение и запись данных в файл
Операции чтения и записи данных в файлы в принципе очень просты, однако выполняются они не через объекты DirectoryInfo и FileInfo. В .NET Framework 4 это делается через объект File. Позже будет показано, как их выполнять с помощью других классов, представляющих обобщенную концепцию потока (stream).
До появления .NET Framework 2.0 вокруг того, как следует производить чтение и запись данных в файлы, велось много споров. Классы из .NET Framework можно было использовать, но такой подход не был простым. В версии .NET Framework 2.0 класс File был расширен; с его помощью стало возможным выполнение операций чтения и записи данных в файлы с помощью всего одной строки кода. То же самое доступно и в .NET Framework 4.
Чтение из файла
Рассмотрим пример чтения файла, создав приложение WPF, которое содержит обычное текстовое поле, кнопку и многострочное текстовое поле. В конечном итоге должна получиться форма, подобная показанной ниже. Используйте следующую разметку:
При построении данного приложения первым шагом является добавление оператора using для включения пространства имен System.IO. После этого с помощью события buttonRead_Click кнопки Прочитать многострочное текстовое поле заполняется содержимым файла. Затем к содержимому файла можно получать доступ методом File.ReadAllText(). Как не трудно заметить, чтение файла осуществляется с помощью единственного оператора.
Метод ReadAllText() открывает указанный файл, считывает его содержимое и затем закрывает его. Возвращаемое значение метода ReadAllText() представляет собой строку, в которой хранится все содержимое указанного файла. Результат может выглядеть примерно так, как показано на рисунке:

Для открытия файлов и работы с ними предназначены также методы ReadAllBytes и ReadAllLines. Метод ReadAllBytes позволяет открывать двоичные файлы и считывать их содержимое в байтовый массив. Упомянутый выше метод ReadAllText возвращает все содержимое файла в виде одной строки. Такое поведение не всегда подходит. Вместо этого может требоваться работать с содержимым файла в построчной манере, т.е. строка за строкой. Для этого служит метод ReadAllLines, который возвращает массив строк с содержимым файла.
Запись в файл
Помимо чтения, в .NET Framework существенно упрощает также и запись данных в файлы. Вдобавок к методам для чтения файлов ReadAllText(), ReadAllLines() и ReadAllBytes() , в ней предлагаются методы, предназначенные для записи данных в файлы — WriteAllText() , WriteAllBytes() иWriteAllLines().
Рассмотрим пример, основанный на том же проекте WPF, но на этот раз многострочное текстовое поле будет использоваться для ввода данных в файл. Код обработчика событий buttonRead_Click теперь должен выглядеть следующим образом:
Скомпонуйте и запустите приложение. Введите в первом текстовом поле значение С:\Testing.txt, а во втором — какой-нибудь текст, после чего щелкните на кнопке. Внешне ничего не произойдет, однако, если заглянуть после этого в корневой каталог на диске С:, то можно будет обнаружить там файл Testing.txt с содержимым, которое было введено во втором текстовом поле.
Метод WriteAllText() создал в указанном каталоге новый текстовый файл, поместил в него предоставленное в поле содержимое и закрыл файл. Неплохо для всего одной строки кода. Если запустить приложение снова и указать тот же файл (Testing.txt), но задать новое содержимое для него, щелчок на кнопке опять заставит приложение выполнить ту же операцию. Новое содержимое не добавляется к старому, а полностью заменяет его. Методы WriteAllText(), WriteAllBytes() и WriteAllLines() перезаписывают предыдущее содержимое файла, поэтому при их применении следует соблюдать осторожность.
Поскольку данные могут записываться не только на диск, но и в другие места (например, в именованные каналы или в память), важно уметь работать с файловым вводом-выводом в .NET, используя потоки в качестве средства для перемещения содержимого файлов. Об этом пойдет речь в следующей статье.
Как записать данные в файл c
Класс FileStream представляет возможности по считыванию из файла и записи в файл. Он позволяет работать как с текстовыми файлами, так и с бинарными.
Создание FileStream
Для создания объекта FileStream можно использовать как конструкторы этого класса, так и статические методы класса File. Конструктор FileStream имеет множество перегруженных версий, из которых отмечу лишь одну, самую простую и используемую:
Здесь в конструктор передается два параметра: путь к файлу и перечисление FileMode . Данное перечисление указывает на режим доступа к файлу и может принимать следующие значения:
Append : если файл существует, то текст добавляется в конец файл. Если файла нет, то он создается. Файл открывается только для записи.
Create : создается новый файл. Если такой файл уже существует, то он перезаписывается
CreateNew : создается новый файл. Если такой файл уже существует, то приложение выбрасывает ошибку
Open : открывает файл. Если файл не существует, выбрасывается исключение
OpenOrCreate : если файл существует, он открывается, если нет — создается новый
Truncate : если файл существует, то он перезаписывается. Файл открывается только для записи.
Другой способ создания объекта FileStream представляют статические методы класса File:
Первый метод открывает файл с учетом объекта FileMode и возвращает файловой поток FileStream. У этого метода также есть несколько перегруженных версий. Второй метод открывает поток для чтения, а третий открывает поток для записи.
Закрытие потока
Класс FileStream для освобождения всех реусрсов, связанных с файлом, реализует интерфейс IDisposable. Соответственно после завершения работы с FileStream необходимо освободить связанный с ним файл вызовом метода Dispose. Для корректного закрытия можно вызвать метод Close() , который вызывает метод Dispose:
Либо можно использовать конструкцию using, которая автоматически освободит все связанные с FileStream ресурсы:
Свойства и методы FileStream
Рассмотрим наиболее важные свойства класса FileStream :
Свойство Length : возвращает длину потока в байтах
Свойство Position : возвращает текущую позицию в потоке
Свойство Name : возвращает абсолютный путь к файлу, открытому в FileStream
Для чтения/записи файлов можно применять следующие методы класса FileStream :
void CopyTo(Stream destination) : копирует данные из текущего потока в поток destination
Task CopyToAsync(Stream destination) : асинхронная версия метода CopyTo
void Flush() : сбрасывает содержимое буфера в файл
Task FlushAsync() : асинхронная версия метода Flush
int Read(byte[] array, int offset, int count) : считывает данные из файла в массив байтов и возвращает количество успешно считанных байтов. Принимает три параметра:
array — массив байтов, куда будут помещены считываемые из файла данные
offset представляет смещение в байтах в массиве array, в который считанные байты будут помещены
count — максимальное число байтов, предназначенных для чтения. Если в файле находится меньшее количество байтов, то все они будут считаны.
Task<int> ReadAsync(byte[] array, int offset, int count) : асинхронная версия метода Read
long Seek(long offset, SeekOrigin origin) : устанавливает позицию в потоке со смещением на количество байт, указанных в параметре offset.
void Write(byte[] array, int offset, int count) : записывает в файл данные из массива байтов. Принимает три параметра:
array — массив байтов, откуда данные будут записываться в файл
offset — смещение в байтах в массиве array, откуда начинается запись байтов в поток
count — максимальное число байтов, предназначенных для записи
Чтение и запись файлов
FileStream представляет доступ к файлам на уровне байтов, поэтому, например, если вам надо считать или записать одну или несколько строк в текстовый файл, то массив байтов надо преобразовать в строки, используя специальные методы. Поэтому для работы с текстовыми файлами применяются другие классы.
В то же время при работе с различными бинарными файлами, имеющими определенную структуру, FileStream может быть очень даже полезен для извлечения определенных порций информации и ее обработки.
Посмотрим на примере считывания-записи в текстовый файл:
Разберем этот пример. Вначале определяем путь к файлу и текст для записи в файл.
И при чтении, и при записи для создания и удаления объекта FileStream используется конструкция using , по завершению которой у созданного объекта FileStream автоматически вызывается метод Dispose , и, таким образом, объект уничтожается.
Поскольку операции с файлами могут занимать продолжительное время и являются узким местом в работе программы, рекомендуется использовать асинхронные версии методов FileStream. И при записи, и при чтении применяется объект кодировки Encoding.Default из пространства имен System.Text . В данном случае мы используем два его метода: GetBytes для получения массива байтов из строки и GetString для получения строки из массива байтов.
В итоге введенная нами строка записывается в файл note.txt . И мы получим следующий консольный вывод:
Записанный файл по сути представляет бинарный файл (не текстовый), хотя если мы в него запишем только строку, то сможем посмотреть в удобочитаемом виде этот файл, открыв его в текстовом редакторе. Однако если мы в него запишем случайные байты, например:
То у нас могут возникнуть проблемы с его пониманием. Поэтому для работы непосредственно с текстовыми файлами предназначены отдельные классы — StreamReader и StreamWriter.
Произвольный доступ к файлам
Нередко бинарные файлы представляют определенную структуру. И, зная эту структуру, мы можем взять из файла нужную порцию информации или наоброт записать в определенном месте файла определенный набор байтов. Например, в wav-файлах непосредственно звуковые данные начинаются с 44 байта, а до 44 байта идут различные метаданные — количество каналов аудио, частота дискретизации и т.д.
С помощью метода Seek() мы можем управлять положением курсора потока, начиная с которого производится считывание или запись в файл. Этот метод принимает два параметра: offset (смещение) и позиция в файле. Позиция в файле описывается тремя значениями:
SeekOrigin.Begin : начало файла
SeekOrigin.End : конец файла
SeekOrigin.Current : текущая позиция в файле
Курсор потока, с которого начинается чтение или запись, смещается вперед на значение offset относительно позиции, указанной в качестве второго параметра. Смещение может быть отрицательным, тогда курсор сдвигается назад, если положительное — то вперед.
Рассмотрим простой пример:
Вначале записываем в файл текст «hello world». Затем снова обращаемся к файла для считывания. Сначала перемещаем курсор на пять символов назад относительно конца файлового потока:

То есть после выполнения этого вызова курсор будет стоять на позиции символа «w».
После этого считываем пять байт начиная с символа «w». В кодировке по умолчанию 1 символ будет представлять 1 байт. Поэтому чтение 5 байт будет эквивалентно чтению пяти сиволов: «world».
Соответственно мы получим следующий консольный вывод:
Рассмотрим чуть более сложный пример — с записью начиная с некоторой позиции:
Здесь также вначале записываем в файл строку «hello world». Затем также открываем файл и опять же перемещаемся в конец файла, не доходя до конца пять символов (то есть опять же с позиции символа «w»), и осуществляем запись строки «house». Таким образом, строка «house» заменяет строку «world».
C#. Иерархия потоков с опорными хранилищами. Класс FileStream.
Иерархия потоков с опорными хранилищами. Класс FileStream . Конструкторы класса FileStream . Создание экземпляров FileStream . Примеры. Обзор базовых методов
Содержание
- 1. Иерархия потоков с опорными хранилищами. Рисунок
- 2. Класс FileStream . Назначение. Создание экземпляра класса FileStream . Перечисления FileMode , FileAccess , FileShare , FileOptions
- 3. Примеры, демонстрирующие создание экземпляров типа FileStream для различных случаев применения
- 3.1. Создание файла. Простейший случай. Пример
- 3.2. Открытие уже существующего файла для считывания. Простейший случай. Пример
- 3.3. Примеры обработки (запись/чтение) файла с указанным видом доступа. Перечисление FileShare
- 3.3.1. Чтение данных из файла разными (параллельными) объектами
- 3.3.2. Запись данных в файл с различных объектов. Пример
- 3.3.3. Пример записи двух чисел в файл и их считывание различными объектами
- 3.3.4. Пример поочередной записи и чтения данных из одного файла. Демонстрация доступа FileShare.ReadWrite
Поиск на других ресурсах:
1. Иерархия потоков с опорными хранилищами. Рисунок
Все классы с опорными хранилищами унаследованы от класса Stream . Основными классами потоков с опорными хранилищами являются следующие:
- FileStream — обеспечивает доступ к файлу. Класс поддерживает синхронную и асинхронную запись и чтение информации;
- MemoryStream — реализует поток, резервным хранилищем которого является память;
- IsolatedStorageFileStream — реализует доступ к файлу, который размещается в изолированном хранилище;
- NetworkStream — обеспечивает основной поток данных для доступа к сети. Этот класс унаследован от класса FileStream ;
- PipeStream — обеспечивает объект типа Stream для основного канала, который поддерживает как анонимные, так и именованные каналы.
На рисунке 1 изображены классы, соответствующие потокам с опорными хранилищами. Классы сгруппированы по пространствам имен.

Рисунок 1. Иерархия потоков с опорными хранилищами
Для того, чтобы использовать средства тому или иному классу нужно предварительно подключить соответствующее пространство имен. Например, чтобы использовать возможности класса NetworkStream в начале модуля программы нужно указать строку
2. Класс FileStream . Назначение. Создание экземпляра класса FileStream . Перечисления FileMode , FileAccess , FileShare , FileOptions
Класс FileStream используется для выполнения различных операций с файлами. Класс содержит средства доступа к файлу. Часть методов и свойств класса наследуется из базового класса Stream .
Класс FileStream поддерживает как синхронные, так и асинхронные операции записи в файл и чтения из файла.
Чтобы создать экземпляр класса FileStream нужно вызвать один из многочисленных конструкторов этого класса. Ниже приведен список некоторых из них.
- path – путь к файлу на диске или ином носителе информации;
- mode — одно из значений перечисления FileMode , которое определяет режим доступа к файлу (Open, Append, Create);
- access — задает доступ к файлу на основании множества констант перечисления FileAccess ;
- share — определяет вид управления доступом к файлу из других объектов на основании набора констант из перечисления FileShare ;
- bufferSize — положительное значение типа System.Int32 , которое определяет размер буфера в байтах. По умолчанию bufferSize = 4096 байт;
- useAsync — определяет, будет ли происходить синхронный или асинхронный ввод/вывод. Следует заметить, что не все операционные системы поддерживают асинхронный ввод/вывод. Если useAsync = true , то обеспечивается асинхронный ввод/вывод;
- options — задает дополнительные опции настройки при создании объекта типа FileStream . Эти опции имеют фиксированные значения и представлены перечислением FileOptions .
В вышеприведенных конструкторах режим открытия файла mode задается из множества значений перечисления FileMode :
- Append — режим открытия файла для добавления. Этот режим можно использовать в сочетании с режимом доступа FileAccess.Write . Если файл существует, то этот файл открывается и указатель чтения из файла перематывается на конец файла. Если файла не существует, то создается новый файл;
- Create — режим создания нового файла. Этот режим используется в сочетании с доступом FileAccess.Write . Если файла не существует, то создается новый файл по указанному пути. Если файл существует, то он будет перезаписан;
- CreateNew — режим, указывает операционной системе создать новый файл. Данный режим используется в сочетании с модификатором доступа FileAccess.Write . Отличие данного режима от FileMode.Create заключается в том, что если файл уже существует, то будет сгенерировано исключение System.IO.IOException ;
- Open — режим открытия уже существующего файла. Доступ к файлу (чтение, запись или чтение/запись) определяется значением из перечисления FileAccess (смотрите ниже). Если файл не существует, то будет сгенерировано исключение System.IO.FileNotFoundException ;
- OpenOrCreate — режим открытия файла. Если файла не существует, то создается новый файл. Файл может быть открыт с любым доступом ( FileAccess.Read , FileAccess.Write , FileAccess.ReadWrite );
- Truncate — задает режим открытия существующего файла с его усечением. Когда файл открыт, он должен быть усечен до нулевого размера. Этот режим сочетается с доступом к файлу FileAccess.Write . Если попытаться прочитать файл в режиме Truncate , будет сгенерировано исключение System.ArgumentException .
Перечисление FileAccess определяет доступ к файлу на основе одной из нижеследующих констант:
- Read — файл доступен для чтения;
- ReadWrite — файл доступен для записи и чтения;
- Write — файл доступен для записи.
Перечисление FileShare определяет управление видом доступа других объектов типа FileStream и может иметь следующие значения:
- Delete — дает разрешение на дальнейшее удаление файла;
- Inheritable — указывает, что дочерние процессы могут наследовать обработчик файла;
- None — запрещает доступ к текущему файлу. Любой запрос на открытие текущего файла будет закрыт;
- Read — разрешает дальнейшее открытие файла для чтения. Если это значение не указано, то любая попытка открыть файл для чтения из другого процесса будет неудачной, пока файл не будет закрыт;
- ReadWrite — дает разрешение на дальнейшее открытие файла для чтения или записи. Если этот флажок не указан, то любая попытка открыть файл с текущего или иного процесса будет неудачной;
- Write — дает разрешение на дальнейшее открытие файла для записи. Если не указать это значение, то любая попытка открыть файл для записи из другого процесса (потока) будет неудачной до тех пор, пока файл не будет закрыт.
В перечислении FileOptions реализованы дополнительные возможности (опции), которые определяют назначение открытого файла:
- Asynchronous — указывает, что файл может быть использован для асинхронного чтения и записи;
- DeleteOnClose — указывает, что файл будет удален после того, как он больше не будет использоваться;
- Encrypted — указывает, что файл зашифрован и его можно расшифровать только с помощью того же аккаунта пользователя, используемого для шифрования;
- None — указывает, что никаких дополнительных опций не применяется для создаваемого объекта типа FileStream ;
- RandomAccess — указывает на то, что к файлу осуществляется случайный доступ. Это указание дает системе подсказку для оптимизации кэширования файлов;
- SequentialScan — указывает на то, что файл читается последовательно от начала до конца. Это указание может быть использовано в качестве подсказки системе осуществить оптимизацию кэширования файлов. Это может дать некоторое повышение производительности;
- WriteThrough — указание системе осуществлять запись через любой промежуточный кэш и переходить непосредственно на диск.
3. Примеры, демонстрирующие создание экземпляров типа FileStream для различных случаев применения
3.1. Создание файла. Простейший случай. Пример
Чтобы создать новый файл, нужно задать режим создания файла FileMode.Create . Особенность этого режима заключается в том, что если файл присутствует и он скрытый (hidden), то будет сгенерировано исключение UnauthorizedAccessException . Поэтому, для создания файла можно писать примерно следующий код:
Создание файла не может быть с режимом доступа FileAccess.Read. Если написать такой код:
то после запуска, программа сгенерирует исключение System.ArgumentException .
3.2. Открытие уже существующего файла для считывания. Простейший случай. Пример
Если файл уже есть на диске, то чтобы считать с него информацию без ее модификации, нужно использовать режим FileMode.Open и доступ FileAccess.Read . Если файла нет на диске, то будет сгенерировано исключение FileNotFoundException . Поэтому, нужно это исключение перехватить.
3.3. Примеры обработки (запись/чтение) файла с указанным видом доступа. Перечисление FileShare
Управление доступностью обрабатываемого файла для других экземпляров определяется в конструкторе класса FileStream . Конструктор получает параметром значение из перечисления FileShare , которое определяет особенности совместного доступа к файлу из разных экземпляров FileStream .
3.3.1. Чтение данных из файла разными (параллельными) объектами
Если нужно, чтобы обрабатываемый файл был считан другим объектом, то нужно указать FileShare.Read как показано далее
3.3.2. Запись данных в файл с различных объектов. Пример
Для того, чтобы одновременно записывать в файл с различных объектов, нужно при создании файлового объекта типа FileStream указать FileShare.Write по образцу ниже
3.3.3. Пример записи двух чисел в файл и их считывание различными объектами
Данный пример демонстрирует:
- запись в файл чисел типа double и int из двух разных экземпляров класса FileStream . При записи учитывается смещение;
- чтение из файла чисел типа double и int из двух разных экземпляров класса FileStream . Как и в случае с записью, учитывается смещение;
- возможности класса BitConverter для конвертирования различных типов в массив byte[] и наоборот;
- использование и сочетание методов Write() и Seek() для записи данных различных типов;
- использование и сочетание методов Read() и Seek() для чтения данных различных типов;
- использование метода ReadByte() для формирования массива типа byte[] .
3.3.4. Пример поочередной записи и чтения данных из одного файла. Демонстрация доступа FileShare.ReadWrite
В примере продемонстрирована одновременная запись и чтение целого числа из общего открытого файла. Предполагается, что файл предварительно создан.