Запись в файл с C#
В этом посте представлен обзор некоторых доступных альтернатив записи текста в файл в C#. Решение должно создать новый файл, если он не существует, и обрезать существующий файл перед записью.
1. Использование File Class
Класс File предоставляет несколько статических служебных методов для записи текста в файл. Ниже приведены примеры каждого из этих методов:
⮚ File.WriteAllText() метод
⮚ File.WriteAllLines() метод
⮚ File.WriteAllBytes() метод
Обратите внимание, что вышеупомянутые методы перезаписывают существующий файл. Чтобы добавить текст в файл, используйте любой из AppendAllLines , AppendAllText , или же AppendText() методы.
2. Использование StreamWriter Class
StreamWriter class предоставляет несколько методов для записи в файл. Мы можем использовать его Write() метод записи строки в поток.
В следующем примере показано, как использовать StreamWriter объект для записи данных в новый файл. Dispose() метод вызывается автоматически для сброса и закрытия потока, поскольку средство записи потока объявляется и создается в операторе using.
Как перезаписывать файл c
Для работы непосредственно с текстовыми файлами в пространстве System.IO определены специальные классы: StreamReader и StreamWriter .
Запись в файл и StreamWriter
Для записи в текстовый файл используется класс StreamWriter . Некоторые из его конструкторов, которые могут применяться для создания объекта StreamWriter:
StreamWriter(string path) : через параметр path передается путь к файлу, который будет связан с потоком
StreamWriter(string path, bool append) : параметр append указывает, надо ли добавлять в конец файла данные или же перезаписывать файл. Если равно true, то новые данные добавляются в конец файла. Если равно false, то файл перезаписываетсяя заново
StreamWriter(string path, bool append, System.Text.Encoding encoding) : параметр encoding указывает на кодировку, которая будет применяться при записи
Свою функциональность StreamWriter реализует через следующие методы:
int Close() : закрывает записываемый файл и освобождает все ресурсы
void Flush() : записывает в файл оставшиеся в буфере данные и очищает буфер.
Task FlushAsync() : асинхронная версия метода Flush
void Write(string value) : записывает в файл данные простейших типов, как int, double, char, string и т.д. Соответственно имеет ряд перегруженных версий для записи данных элементарных типов, например, Write(char value) , Write(int value) , Write(double value) и т.д.
Task WriteAsync(string value) : асинхронная версия метода Write. Обратите внимание, что асинхронные версии есть не для всех перегрузок метода Write.
void WriteLine(string value) : также записывает данные, только после записи добавляет в файл символ окончания строки
Task WriteLineAsync(string value) : асинхронная версия метода WriteLine
Рассмотрим запись в файл на примере:
В данном случае два раза создаем объект StreamWriter. В первом случае если файл существует, то он будет перезаписан. Если не существует, он будет создан. И в нее будет записан текст из переменной text. Во втором случае файл открывается для дозаписи, и будут записаны атомарные данные — строка и число.
По завершении в папке программы мы сможем найти файл note.txt, который будет иметь следующие строки:
В пример выше будет использоваться кодировка по умолчанию. но также можно задать ее явным образом:
Чтение из файла и StreamReader
Класс StreamReader позволяет нам легко считывать весь текст или отдельные строки из текстового файла.
Некоторые из конструкторов класса StreamReader:
StreamReader(string path) : через параметр path передается путь к считываемому файлу
StreamReader(string path, System.Text.Encoding encoding) : параметр encoding задает кодировку для чтения файла
Среди методов StreamReader можно выделить следующие:
void Close() : закрывает считываемый файл и освобождает все ресурсы
int Peek() : возвращает следующий доступный символ, если символов больше нет, то возвращает -1
int Read() : считывает и возвращает следующий символ в численном представлении. Имеет перегруженную версию: Read(char[] array, int index, int count) , где array — массив, куда считываются символы, index — индекс в массиве array, начиная с которого записываются считываемые символы, и count — максимальное количество считываемых символов
streamWriter rewrite the file or append to the file
StreamWriters default behavior is to create a new file, or overwrite it if it exists. To append to the file you’ll need to use the overload that accepts a boolean and set that to true. In your example code, you will rewrite test.txt 5 times.
You can pass a second parameter to StreamWriter to enable or disable appending to file:
![]()
You can start by using the FileStream and then passing that to your StreamWriter.
So what is the result of your code?
I would expect the file to contain nothing but the number 4, since the default behavior is to create/overwrite, but you are saying that it is not overwriting?
You should be able to make it overwrite the file by doing what you are doing, and you can append by making a FileStream with FileMode.Append.
-
The Overflow Blog
Linked
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
Site design / logo © 2023 Stack Exchange Inc; user contributions licensed under CC BY-SA . rev 2023.3.11.43304
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
Работа с классом FileStream в C#
Класс FileStream предоставляет возможности для чтения и записи файлов как с использованием синхронных, так и асинхронных методов. Этот класс может работать как с текстовыми, так и с бинарными файлами.
Как создать FileStream (открыть файл)
Класс FileStream имеет достаточно много перегруженных версий конструкторов, рассмотрим только некоторые из них, которые используются наиболее часто, а также параметры, используемые в этих конструкторах.
Конструктор с двумя параметрами: путь и способ открытия файла (FileMode)
Наиболее распространенный способ создания FileStream :
path — относительный или абсолютный путь к файлу;
mode — способ открытия файла. Здесь FileMode — это перечисление, имеющее следующие поля:
| Имя | Значение | Описание |
|---|---|---|
| CreateNew | 1 | Создавать новый файл. Если файл уже существует, создается исключение IOException . |
| Create | 2 | Создавать новый файл. Если файл уже существует, он будет перезаписан. Если файл уже существует, но является скрытым, создается исключение UnauthorizedAccessException . |
| Open | 3 | Открыть существующий файл. Если файл не существует, то создается исключение FileNotFoundException |
| OpenOrCreate | 4 | Открыть файл, если он существует, в противном случае должен быть создан новый файл. |
| Truncate | 5 | Открыть и перезаписать существующий файл. Файл становится доступным только для записи. Попытка выполнить чтение из файла, открытого с помощью FileMode.Truncate , вызывает исключение ArgumentException . |
| Append | 6 | Открывает файл, если он существует, и находит конец файла, либо создает новый файл. Файл открывается только для записи. |
Пример открытия файла с использованием этого конструктора:
Конструктор с тремя параметрами: путь, способ открытия файла (FileMode) и доступ к файлу (FileAccess)
Второй вариант конструктора FileStream :
здесь FileAccess перечисление, которое определяет, каким образом объект FileStream может получить доступ к файлу (чтение, запись, чтение и запись) и содержит следующие поля:
| Имя | Значение | Описание |
|---|---|---|
| Read | 1 | Доступ для чтения файла |
| ReadWrite | 3 | Доступ для чтения и записи файла |
| Write | 2 | Доступ для записи в файл |
Пример открытия файла с использование этого конструктора:
Конструктор с четырьмя параметрами: путь, способ открытия файла (FileMode), доступ к файлу (FileAccess), способ совместного использования (FileShare)
здесь FileShare — перечисление, определяющее способ совместного использования файла:
| Имя | Значение | Описание |
|---|---|---|
| Delete | 4 | Разрешает последующее удаление файла. |
| Inheritable | 16 | Разрешает наследование дескриптора файла дочерними процессами. |
| None | 0 | Отклоняет совместное использование текущего файла. |
| Read | 1 | Разрешает последующее открытие файла для чтения. |
| ReadWrite | 3 | Разрешает последующее открытие файла для чтения или записи. |
| Write | 2 | Разрешает последующее открытие файла для записи. |
Обычно это перечисление используется для определения того, могут ли два процесса одновременно считывать данные из одного файла. Так, например, если файл открыт с указанием FileShare.Read , то другие пользователи могут открыть файл для чтения, но не для записи. В примере ниже показано открытие файла с запретом на его использование другими процессами:
Кроме перечисленных выше конструкторов FileStream , файл можно также открыть, используя методы класса FileInfo , а также использовать другие виды конструкторов FileStream , например, указывая дополнительные параметры открытия файла, используя перечисление FileOptions .
Закрытие файлового потока
FileStream реализует интерфейс IDisposable . Это означает, что по окончании использования объекта, выделенную ему память следует прямо или косвенно освободить.
Прямое освобождение памяти может быть реализовано следующим образом:
Косвенное освобождение памяти может быть реализовано с использованием конструкции using следующим образом:
После выполнения всех необходимых операций с файлом память будет освобождена автоматически и нам не потребуется вызывать методы Close() или Dispose() .
Свойства и методы FileStream
Свойства FileStream
Рассмотрим основные свойства и методы FileStream .
| Свойство | Значение | Описание |
|---|---|---|
| Can Read | bool | Получает значение, указывающее, поддерживает ли текущий поток чтение. |
| Can Seek | bool | Получает значение, указывающее, поддерживает ли текущий поток поиск (произвольное перемещение) по файлу. |
| CanWrite | bool | Получает значение, указывающее, поддерживает ли текущий поток запись. |
| Is Async | bool | Получает значение, указывающее, как был открыт FileStream : синхронно или асинхронно. |
| Length | long | Получает длину потока в байтах. |
| Name | string | Получает абсолютный путь к файлу, открытому в функции FileStream . |
| Position | long | Возвращает или задает текущую позицию этого потока. |
Попробуем открыть какой-либо файл на диске и прочитать его свойства:
В результате, получим следующий вывод консоли:
Следует отметить, что значение IsAsync = false не означает, что мы не сможем вызывать асинхронные методы чтения/записи файла. Однако, фактически, эти операции будут выполняться синхронно, несмотря на то, что интерфейс не будет блокироваться (см. описание свойства на сайте Microsoft). Чтобы открыть файл в асинхронном режиме, можно воспользоваться следующим конструктором FileStream :
В пятом параметре мы указали число 4096 — размер буфера (об этом поговорим ниже), а шестым — указали дополнительную настройку доступа к файлу, а именно — открыть файл в асинхронном режиме.
Методы FileStream
Ниже в таблице представлены основные методы работы с FileStream и их асинхронные аналоги
| Метод | Асинхронный аналог | Описание |
| Copy To() | Copy ToAsync() | Копирует данные из текущего потока и записывает их в другой поток. Методы имеют перегруженные версии |
| Dispose() | Dispose Async() | Освобождает неуправляемые ресурсы, используемые объектом FileStream , а при необходимости освобождает также управляемые ресурсы. |
| Flush() | Flush Async() | Очищает буферы потока и вызывает запись всех буферизованных данных в файл. |
| Lock() | Запрещает другим процессам чтение объекта FileStream и запись в этот объект. | |
| Unlock() | Разрешает доступ других процессов ко всему ранее заблокированному файлу или его части. | |
| Read() | Read Async() | Выполняет чтение блока байтов из потока и запись данных в заданный буфер. Методы имеют перегруженные версии |
| Read Byte() | Считывает байт из файла и перемещает положение чтения на один байт. | |
| Seek(Int64, Seek Origin) | Устанавливает текущее положение этого потока на заданное значение. | |
| Set Length() | Устанавливает длину этого потока на заданное значение. | |
| Write() | Write Async() | Записывает блок байтов в файловый поток. Методы имеют перегруженные версии |
| Write Byte() | Запись байта в текущую позицию в потоке файла. |
Запись и чтение файлов с использованием FileStream
FileStream позволяет нам производить чтение и запись данных на уровне байтов для чего необходимо использовать буфер — массив byte[] . Соответственно, все данные, которые мы хотим прочитать/записать с использованием FileStream должны быть представлены (в случае записи) или будут представлены (в случае чтения) в виде массива. Отсюда вытекает некоторое неудобство при работе с использованием FileStream , например, с текстовыми файлами для которых в .NET C# предусмотрены другие, более удобные классы, работу которых мы также рассмотрим. При этом, FileStream может оказаться достаточно удобным способом доступа к бинарным файлам, особенно в случае, когда нам требуется произвольный доступ и чтение определенной части файла или же при шифровании файлов.
Запись файла
Для начала рассмотрим пример записи строки в файл.
Для записи строки в файл мы используем кодировку по умолчанию (Unicode (UTF-8). Как было сказано выше, перед записью мы должны представить строку в виде массива байтов. Для этого мы воспользовались методом класса Encoding.GetBytes() . После того, как мы получили массив байтов, мы передаем его в синхронный метод Write для записи файла.
FileStream представляет собой бинарный файл. поэтому, мы можем записывать в него не только строки, но и, в принципе, любые данные. Например,
Теперь, если открыть файл в текстовом редакторе, то вместо строки и двух чисел мы увидим набор непонятных символов.
Чтение файла
В примере выше мы записали в файл строку и два байта в результате чего файл стал «нечитабельным». Попробуем провести обратную операцию с этим же файлом — прочитаем строку и два байта и выведем полученные значения в консоль.
В результате, в консоли увидим следующий текст:
Чтение больших файлов и работа с буфером
Данные из файла считываются порциями в буфер. При этом, по умолчанию, при создании FileStream размер буфера устанавливается равным 4096 байт. В большинстве случаев, такого размера вполне хватает для комфортной работы с файлом. При этом, может потребоваться чтение больших файлов, размер которых может достигать сотен мегабайт или даже гигабайт. В этом случае, создание буфера такого размера может оказаться губительным для вашего приложения. Чтобы прочитать большой файл, мы можем задать определенный размер буфера и считывать банные в цикле до тех пор пока не будет достигнут конец файла. Один из возможных вариантов такой работы представлен ниже:
Произвольный доступ к данным
Чаще всего, бинарные файлы имеют какую-либо определенную структуру. Например, в файле базы данных SQLite первые 100 байт выделяются на различные мета-данные — указание версии, размера страницы и т.д. Зная структуру файла, мы можем получать произвольный доступ к частям файла и получать или записывать необходимые данные. Для произвольного доступа к определенной части файла используется метод Seek(Int64, Seek Origin) . В этом методе первый параметр — это количество байт на которые необходимо сместить курсор в потоке, а второй параметр — это перечисление, которое указывает относительно какой позиции необходимо смещаться:
| Begin | 0 | Смещение относительно начала потока |
| Current | 1 | Смещение относительно текущего положения в потоке |
| End | 2 | Смещение с конца потока |
Рассмотрим произвольный доступ к данным файла на следующем примере примере.
В консоли мы увидим следующие числа:
Рассмотрим подробно, как происходило чтение файла:
1. Вначале мы смещаемся относительно начала файла на 2 байта. Курсор становится в позиции, указанной на рисунке красной линией:

2. Производится чтение 1 символа в буфер — курсор смещается на одну позицию вправо:

3. Смещаемся на два байта относительно текущей позиции

4. Производится чтение 1 символа в буфер — курсор смещается на одну позицию вправо: