Лекция 11. Файлы в С#
Основные понятия. Классы .NET для работы с файлами
Файл - именованная информация на внешнем носителе (на жестком или гибком магнитном диске).
Логически файл можно представить как конечное количество последовательных байтов, поэтому такие устройства, как дисплей, клавиатура и принтер, также можно рассматривать как частные случаи файлов.
Передача данных с внешнего устройства в оперативную память называется
чтением, или вводом, обратный процесс – записью, или выводом.
Чтение-запись в языке C# выполняется с помощью подсистемы ввода-вывода и классов библиотеки .NET. Рассмотрим обмен данными с файлами с помощью консоли, который реализуется с помощью потоков.
Поток (stream) – это любой перенос данных от источника к приемнику.
Потоки обеспечивают работу как со стандартными, так и с определенными пользователем типами данных. Поток определяется как последовательность байтов и не зависит от конкретного устройства, с которым производиться обмен (оперативная память, файл на диске, клавиатура и принтер).
Обмен с потоком для повышения скорости передачи данных производиться, как правило, через специальную область оперативной памяти – буфер. Буфер выделяется для каждого открытого файла.
При записи в файл вся информация сначала направляется в буфер и там накапливается до тех пор, пока весь буфер не заполниться. Только после этого и после специальной команды сброса происходит передача данных на внешнее устройство.
При чтении из файла данные вначале считываются в буфер, причем не столько, сколько запрашивается, а сколько помещается в буфер. Механизм буферизации позволяет более быстро и эффективно обмениваться информацией с внешними устройствами.
Для поддержки потоков библиотека .NET содержит иерархию классов, основная часть которой представлена на рисунке 1 Эти классы определены в пространстве имён System.IO.
Object
MarshalByRefObject FileSystemInfo DirectoryInfo
BinaryReader |
|
|
|
|
|
FileInfo |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BinaryWriter |
|
|
|
|
|
BufferedStream |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Directory |
|
|
|
Stream |
|
FileStream |
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
File |
|
|
|
|
|
MemoryStream |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StreamReader |
|
|
|
|
TextReader |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StringReader |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StreamWriter |
|
|
|
|
TextWriter |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StringWriter |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Рисунок 1: Основные классы пространства имен System.IO
Классы библиотеки позволяют работать в различных режимах с файлами, каталогами и областями оперативной памяти. Краткое описание классов приведено в таблице 1.
|
Таблица 1: Основные классы пространства имен System.IO |
|
|
|
|
Класс |
|
Описание |
BinaryReader |
|
Чтение и запись значений простых |
|
встроенных типов |
|
|
|
|
BinaryWriter |
|
(целочисленных, логических, строковых) во |
|
внутренней форме представления |
|
|
|
|
|
|
Временное хранение потока байтов |
BufferedStream |
|
(например, для последующего переноса в |
|
|
постоянное хранилище) |
Directory |
|
Работа с каталогами или физическими |
|
файлами: создание, |
|
|
|
|
DirectoryInfo, File, |
|
удаление, получение свойств. Возможности |
|
классов File и |
|
|
|
|
|
|
Directory реализованы в основном в виде |
FileInfo |
|
статических методов.Аналогичные классы |
|
DirectoryInfo и FileInfo используют обычные |
|
|
|
|
|
|
методы |
|
|
|
FileStream |
|
Произвольный (прямой) доступ к файлу, |
|
представленному как поток байтов |
|
|
|
|
MemoryStream |
|
Произвольный доступ к потоку байтов в |
|
оперативной памяти |
|
|
|
|
StreamWriter |
|
Чтение из файла и запись в файл текстовой |
|
информации |
|
|
|
|
StreamReader |
|
( произвольный доступ не поддерживается ) |
|
|
|
StringReader |
|
Работа с текстовой информацией в |
|
оперативной памяти SrringReader |
|
|
|
1Виды файлов
Информация, обрабатываемая фалами в С# может быть в виде:
•двоичного представления данных(классы BinaryReader, BinaryWriter);
•байтов (FileStream);
•текста, то есть символов (StreamWriter, StreamReader).
Двоичные и байтовые потоки хранят данные в том же виде, в котором они представлены в оперативной памяти, то есть при обмене с файлом происходит побитовое копирование информации. Двоичные файлы применяются не для просмотра их человеком, а для использования в программах.
Соответственно, различают текстовые, двоичные и байтовые файлы.
Доступ к файлам может быть последовательным, когда очередной элемент можно прочитать (записать) только после аналогичной операции с предыдущим элементом, и произвольным (прямым), при котором выполняется чтение (запись) произвольного элемента по заданному адресу.
Текстовые файлы позволяют выполнять только последовательный доступ, в двоичных и байтовых потоках можно использовать оба метода.
Методы форматированного ввода (ЧТЕНИЯ) значений арифметических типов, в С# не поддерживаются. Для преобразования из символьного в числовое представление используются методы класса Convert или метод Parse.
Форматированный вывод (ЗАПИСЬ) выполняется с помощью метода ToString, результаты выполнения которых передаются в методы текстовых файлов.
Алгоритм работы с файловыми потоками
Использование классов файловых потоков в программе предполагает следующие операции:
1.Создание потока и связывание его с физическим файлом.
2.Обмен информацией (чтение-запись).
3.Закрытие файла.
Каждый файл можно создавать разными способами и открывать в различных режимах (файлы можно открывать для чтения, только для записи или для чтения и записи).
Режимы доступа к файлу содержаться в переменной FileAccess, определенном в пространстве имен System.IO. Константы перечисления приведены в таблице 2.
|
|
Таблица 2: Значения перечисления FileAccess |
|
|
|
|
|
Значение |
|
Описание |
|
Read |
|
Открыть файл только для чтения |
|
|
|
|
|
ReadWrite |
|
Открыть файл для чтения и записи |
|
|
|
|
|
Write |
|
Открыть файл для записи |
|
|
|
|
|
Возможные режимы открытия файла определены в переменной FileMode (таблица 3) |
|||
|
|
Таблица 3: Значения перечисления FileMode |
|
|
|
|
|
Значение |
|
Описание |
|
|
Открыть файл, если он существует, и установить текущий |
||
Append |
указатель в конец файла. Если файл не существует, создать |
||
|
новый файл |
||
Create |
Создать новый файл. Если в каталоге уже существует файл с |
||
таким же именем, он будет стерт |
|||
|
|||
CreateNew |
Создать новый файл. Если в каталоге уже существует файл с |
||
таким же именем, возникает исключение IOException |
|||
|
|||
Open |
Открыть существующий файл |
||
OpenOrCreate |
Открыть файл, если он существует. Если нет создать файл с |
||
таким именем |
|||
|
|||
Truncate |
Открыть существующий файл. После открытия он должен |
||
быть обрезан до нулевой длины |
|||
|
Режим FileMode.Append можно использовать только совместно с доступом типа FileAccess.Write, то есть для файлов, открываемых для записи.
2Байтовые файлы (потоки)
Ввод-вывод в файл на уровне байтов выполняется с помощью класса FileStream, основные элементы которого представлены в таблице 4.
|
Таблица 4: Методы и свойства класса Stream |
|
|
|
|
Элемент |
Описание |
|
BeginRead, |
Начать асинхронный ввод или вывод |
|
BeginWrite |
|
|
CanRead, |
Свойства, определяющие, какие операции поддерживает |
|
|
||
CanSeek, |
||
поток: чтение, прямой доступ и/или запись |
||
|
||
CanWrite |
||
|
||
|
|
|
Close |
Закрыть текущий поток и освободить связанные с ним |
|
ресурсы (сокеты, указатели на файлы) |
||
|
||
EndRead, |
Ожидать завершения асинхронного ввода; |
|
|
|
|
EndWrite |
Закончить асинхронный вывод |
|
|
Записать данные из буфера в связанный с потоком источник |
|
Flush |
данных и очистить буфер. Если для данного потока буфер не |
|
|
используется, то этот метод ничего не делает |
|
Length |
Возвратить длину потока в байтах |
|
|
|
|
Position |
Возвратить текущую позицию в потоке |
|
|
|
|
Read |
Считать последовательность байтов (или один байт) из |
|
текущего потока |
||
|
||
ReadByte |
и переместить указатель в потоке на количество считанных |
|
байтов |
||
|
||
|
|
|
Seek |
Установить текущий указатель потока на заданную позицию |
|
SetLength |
Установить длину текущего потока |
|
Write |
Записать последовательность байтов (или один байт) в |
|
текущий поток |
||
|
||
WriteByte |
и переместить указатель в потоке на количество записанных |
|
байтов |
||
|
Байтовый поток класс FileStream реализует эти элементы для работы с дисковыми файлами. Для определения режимов работы с файлом используются стандартные перечисления
FileMode, FileAccess.
Пример работы с байтовым файлом
using System;
using System.Collections.Generic; using System.Linq;
using System.Text; using System.IO;
namespace file_app
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Приложение 1";
//создание файловой переменной и связывание с байтовым потоком - //файлом на диске с именем est.txt
//файл создается для чтения и записи
FileStream f = new FileStream("est.txt",FileMode.Create, FileAccess.ReadWrite);
f.WriteByte(100); //в начало файла записывается число 100 byte[] x = new byte[10];
for (byte i = 0; i < 10; ++i)
{
x[i] = (byte)(10 - i);
f.WriteByte(i); // записывается 10 чисел от 0 до 9
}
f.Write(x, 0, 5); //записывается 5 элементов массива byte[] y = new byte[20];
f.Seek(0, SeekOrigin.Begin); //текущий указатель – на начало
f.Read(y, 0, 20); |
//чтение из |
файла в массив |
foreach (byte elem in y) Console.Write(" |
" + elem); |
Console.WriteLine();
f.Seek(5, SeekOrigin.Begin); //текущий указатель – на 5-ый элемент
int a = f.ReadByte(); |
//чтение 5-го элемента |
Console.WriteLine(a); |
|
a = f.ReadByte(); |
//чтение 6-го элемента |
Console.WriteLine(a); |
|
Console.WriteLine("Текущая позиция в потоке " + f.Position); Console.ReadKey();
f.Close();
}
}
}
Результат работы программы:
Рисунок 2:
Текущая позиция в потоке первоначально устанавливается на начало файла (для любого режима открытия, кроме Append) и сдвигается на одну позицию при записи каждого байта.
Для установки желаемой позиции чтения используется метод Seek, имеющий два параметра: первый задает смещение в байтах относительно точки отсчета, задаваемой вторым. Точки отсчёта задаются константами перечисления SeekOrigin: начало файла – Begin, текущая позиция – Current и конец файла – End.
В данном примере файл создавался в текущем каталоге. Можно указать и полный путь к файлу, при этом удобнее использовать дословные литералы, например
FileStream f=new FileStream (@”D:\C#\test.txt”, FileMode.Create, FileAccess.ReadWrite);
В дословных литералах не требуется дублировать обратную косую черту.