Нет файла
Содержание
FileStream. Чтение и запись файла
Последнее обновление: 13.10.2019
Класс FileStream представляет возможности по считыванию из файла и записи в файл. Он позволяет работать как с текстовыми файлами, так и с бинарными.
Создание FileStream
Для создания объекта FileStream можно использовать как конструкторы этого класса, так и статические методы класса File. Конструктор FileStream имеет множество перегруженных версий, из которых отмечу лишь одну, самую простую и используемую:
FileStream(string filename, FileMode mode)
Здесь в конструктор передается два параметра: путь к файлу и перечисление FileMode. Данное перечисление указывает на режим доступа к файлу и может принимать следующие значения:
-
Append: если файл существует, то текст добавляется в конец файл. Если файла нет, то он создается. Файл открывается только для записи.
-
Create: создается новый файл. Если такой файл уже существует, то он перезаписывается
-
CreateNew: создается новый файл. Если такой файл уже существует, то он приложение выбрасывает ошибку
-
Open: открывает файл. Если файл не существует, выбрасывается исключение
-
OpenOrCreate: если файл существует, он открывается, если нет — создается новый
-
Truncate: если файл существует, то он перезаписывается. Файл открывается только для записи.
Другой способ создания объекта FileStream представляют статические методы класса File:
FileStream File.Open(string file, FileMode mode); FileStream File.OpenRead(string file); FileStream File.OpenWrite(string file);
Первый метод открывает файл с учетом объекта FileMode и возвращает файловой поток FileStream. У этого метода также есть несколько перегруженных версий. Второй метод открывает поток для чтения, а третий открывает поток для записи.
Свойства и методы FileStream
Рассмотрим наиболее важные его свойства и методы класса FileStream:
-
Свойство Length: возвращает длину потока в байтах
-
Свойство Position: возвращает текущую позицию в потоке
-
void CopyTo(Stream destination): копирует данные из текущего потока в поток destination
-
Task CopyToAsync(Stream destination): асинхронная версия метода CopyToAsync
-
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 — максимальное число байтов, предназначенных для записи
-
-
ValueTask WriteAsync(byte array, int offset, int count): асинхронная версия метода Write
Чтение и запись файлов
FileStream представляет доступ к файлам на уровне байтов, поэтому, например, если вам надо считать или записать одну или несколько строк в текстовый файл, то массив байтов надо преобразовать в строки, используя специальные методы. Поэтому для работы с текстовыми файлами применяются другие классы.
В то же время при работе с различными бинарными файлами, имеющими определенную структуру, FileStream может быть очень даже полезен для извлечения определенных порций информации и ее обработки.
Посмотрим на примере считывания-записи в текстовый файл:
using System; using System.IO; namespace HelloApp { class Program { static void Main(string args) { // создаем каталог для файла string path = @»C:\SomeDir2″; DirectoryInfo dirInfo = new DirectoryInfo(path); if (!dirInfo.Exists) { dirInfo.Create(); } Console.WriteLine(«Введите строку для записи в файл:»); string text = Console.ReadLine(); // запись в файл using (FileStream fstream = new FileStream($»{path}\note.txt», FileMode.OpenOrCreate)) { // преобразуем строку в байты byte array = System.Text.Encoding.Default.GetBytes(text); // запись массива байтов в файл fstream.Write(array, 0, array.Length); Console.WriteLine(«Текст записан в файл»); } // чтение из файла using (FileStream fstream = File.OpenRead($»{path}\note.txt»)) { // преобразуем строку в байты byte array = new byte; // считываем данные fstream.Read(array, 0, array.Length); // декодируем байты в строку string textFromFile = System.Text.Encoding.Default.GetString(array); Console.WriteLine($»Текст из файла: {textFromFile}»); } Console.ReadLine(); } } }
Разберем этот пример. Вначале создается папка для файла. Кроме того, на уровне операционной системы могут быть установлены ограничения на запись в опрееделенных каталогах, и при попытке создания и записи файла в подобных каталогах мы получим ошибку.
И при чтении, и при записи используется оператор using. Не надо путать данный оператор с директивой using, которая подключает пространства имен в начале файла кода. Оператор using позволяет создавать объект в блоке кода, по завершению которого вызывается метод Dispose у этого объекта, и, таким образом, объект уничтожается. В данном случае в качестве такого объекта служит переменная fstream.
И при записи, и при чтении применяется объект кодировки Encoding.Default из пространства имен System.Text. В данном случае мы используем два его метода: GetBytes для получения массива байтов из строки и GetString для получения строки из массива байтов.
В итоге введенная нами строка записывается в файл note.txt. По сути это бинарный файл (не текстовый), хотя если мы в него запишем только строку, то сможем посмотреть в удобочитаемом виде этот файл, открыв его в текстовом редакторе. Однако если мы в него запишем случайные байты, например:
fstream.WriteByte(13); fstream.WriteByte(103);
То у нас могут возникнуть проблемы с его пониманием. Поэтому для работы непосредственно с текстовыми файлами предназначены отдельные классы — StreamReader и StreamWriter.
Хотя в данном простеньком консольном приложении, но в реальных приложениях рекомендуется использовать асинхронные версии методов FileStream, поскольку операции с файлами могут занимать продолжительное время и являются узким местом в работе программы. Например, изменим выше приведенную программу, применив асинхронные методы:
using System; using System.IO; using System.Threading.Tasks; namespace HelloApp { class Program { static async Task Main(string args) { // создаем каталог для файла string path = @»C:\SomeDir3″; DirectoryInfo dirInfo = new DirectoryInfo(path); if (!dirInfo.Exists) { dirInfo.Create(); } Console.WriteLine(«Введите строку для записи в файл:»); string text = Console.ReadLine(); // запись в файл using (FileStream fstream = new FileStream($»{path}\note.txt», FileMode.OpenOrCreate)) { byte array = System.Text.Encoding.Default.GetBytes(text); // асинхронная запись массива байтов в файл await fstream.WriteAsync(array, 0, array.Length); Console.WriteLine(«Текст записан в файл»); } // чтение из файла using (FileStream fstream = File.OpenRead($»{path}\note.txt»)) { byte array = new byte; // асинхронное чтение файла await fstream.ReadAsync(array, 0, array.Length); string textFromFile = System.Text.Encoding.Default.GetString(array); Console.WriteLine($»Текст из файла: {textFromFile}»); } Console.ReadLine(); } } }
Произвольный доступ к файлам
Нередко бинарные файлы представляют определенную структуру. И, зная эту структуру, мы можем взять из файла нужную порцию информации или наоброт записать в определенном месте файла определенный набор байтов. Например, в wav-файлах непосредственно звуковые данные начинаются с 44 байта, а до 44 байта идут различные метаданные — количество каналов аудио, частота дискретизации и т.д.
С помощью метода Seek() мы можем управлять положением курсора потока, начиная с которого производится считывание или запись в файл. Этот метод принимает два параметра: offset (смещение) и позиция в файле. Позиция в файле описывается тремя значениями:
-
SeekOrigin.Begin: начало файла
-
SeekOrigin.End: конец файла
-
SeekOrigin.Current: текущая позиция в файле
Курсор потока, с которого начинается чтение или запись, смещается вперед на значение offset относительно позиции, указанной в качестве второго параметра. Смещение может быть отрицательным, тогда курсор сдвигается назад, если положительное — то вперед.
Рассмотрим на примере:
using System.IO; using System.Text; class Program { static void Main(string args) { string text = «hello world»; // запись в файл using (FileStream fstream = new FileStream(@»D:\note.dat», FileMode.OpenOrCreate)) { // преобразуем строку в байты byte input = Encoding.Default.GetBytes(text); // запись массива байтов в файл fstream.Write(input, 0, input.Length); Console.WriteLine(«Текст записан в файл»); // перемещаем указатель в конец файла, до конца файла- пять байт fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока // считываем четыре символов с текущей позиции byte output = new byte; fstream.Read(output, 0, output.Length); // декодируем байты в строку string textFromFile = Encoding.Default.GetString(output); Console.WriteLine($»Текст из файла: {textFromFile}»); // worl // заменим в файле слово world на слово house string replaceText = «house»; fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока input = Encoding.Default.GetBytes(replaceText); fstream.Write(input, 0, input.Length); // считываем весь файл // возвращаем указатель в начало файла fstream.Seek(0, SeekOrigin.Begin); output = new byte; fstream.Read(output, 0, output.Length); // декодируем байты в строку textFromFile = Encoding.Default.GetString(output); Console.WriteLine($»Текст из файла: {textFromFile}»); // hello house } Console.Read(); } }
Консольный вывод:
Текст записан в файл Текст из файла: worl Текст из файла: hello house
Вызов fstream.Seek(-5, SeekOrigin.End) перемещает курсор потока в конец файлов назад на пять символов:
То есть после записи в новый файл строки «hello world» курсор будет стоять на позиции символа «w».
После этого считываем четыре байта начиная с символа «w». В данной кодировке 1 символ будет представлять 1 байт. Поэтому чтение 4 байтов будет эквивалентно чтению четырех сиволов: «worl».
Затем опять же перемещаемся в конец файла, не доходя до конца пять символов (то есть опять же с позиции символа «w»), и осуществляем запись строки «house». Таким образом, строка «house» заменяет строку «world».
Закрытие потока
В примерах выше для закрытия потока применяется конструкция using. После того как все операторы и выражения в блоке using отработают, объект FileStream уничтожается. Однако мы можем выбрать и другой способ:
8.3.1. Редактирование файла /etc/fstab
Файловая система — один из важнейших общесистемных сервисов. Монтирование основных файловых систем осуществляется на этапе загрузки системы. Другие (дополнительные) файловые системы монтируются командой mount, которая была рассмотрена в разд. 4.8. Конфигурационным файлом для команды монтирования является файл /etc/fstab, который тоже был рассмотрен в разд. 4.8. Поэтому здесь не будем повторяться, а ограничимся тем, что приведем несколько советов, которые позволят несколько снизить трудоемкость процедур монтирования файловых систем.
Одним из неудобств ОС Linux по сравнению с Windows является необходимость монтировать файловую систему при работе с дискетами и вообще сменными накопителями (CD-ROM, Zip фирмы Iomega и т.п.). Каждый раз при смене диска приходится заново монтировать и размонтировать файловую систему. Впрочем, и для получения доступа к некоторым разделам жесткого диска тоже необходимо выполнять команды монтирования, если только не заставить систему делать это автоматически, изменив соответствующим образом файл /etc/fstab.
Чтобы не повторять одинаковых действий при каждом перезапуске системы и сократить число необходимых символов, которые приходится вводить с клавиатуры при выполнении операций монтирования, целесообразно выполнить следующее. Сначала создайте точки монтирования (пустые каталоги) для каждого из устройств или внешних файловых систем, которые вы будете периодически подключать: гибкого диска, CD-ROM, ZIP-диска, сетевых дисков, которые будут подключаться по NFS. Это можно сделать из Midnight Commander или следующими командами:
# cd /mnt
# mkdir floppy; mkdir cdrom; mkdir win; mkdir zip; mkdir server
Теперь отредактируйте файл /etc/fstab, добавив в него следующие строки, соответствующие тем устройствам, которые имеются в вашей системе (то, что в файле было до вас, лучше не трогать):
/dev/fd0 /mnt/floppy vfat user,noauto 0 1
/dev/cdrom /mnt/cdrom iso9660 ro,user,noauto 1
/dev/sda4 /mnt/zip vfat user,noauto,exec 0 1
/dev/hda1 /mnt/win vfat user,noauto 0 1 server:/export /mnt/server nfs defaults
Редактирование файла /etc/fstab можно выполнить и с помощью программы linuxconf, о которой будет рассказано ниже (команда File systems | Access local drive).
Если правильно настроен файл /etc/fstab, то обращение к гибкому диску или дискам CD-ROM осуществляется довольно просто. В графической среде KDE чтобы смонтировать диск надо просто щелкнуть правой кнопкой мыши по соответствующему значку и выбрать в появившемся меню команду Монтировать. Чтобы добиться примерно такого же эффекта в программе Midnight Commander, надо добавить в меню этой программы (файл /usr/lib/mc/mc.mnu) команды монтирования и размонтирования дисков. Вот пример таких команд для гибких дисков:
m Смонтировать дискету
mount /mnt/floppy
d Размонтировать дискету
umount /mnt/floppy
(предполагается, что в файле /etc/fstab прописана строка, определяющая устройство и тип файловой системы для /mnt/floppy). После этого смена дискеты в Midnight Commander под Linux будет ничем не сложнее аналогичного действия в программе Norton Commander или в FAR под MS Windows: для того, чтобы смонтировать дискету, достаточно будет последовательно нажать клавиши ‹F2› и ‹M›, для размонтирования — клавиши ‹F2› и ‹D›.
Поделитесь на страничке
Следующая глава >
Добавить комментарий