Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие СS_Шульга.doc
Скачиваний:
48
Добавлен:
12.02.2015
Размер:
703.49 Кб
Скачать

Глава 10. Файлы

Файл – это именованная область памяти на внешнем носителе. Логически любой файл можно представить как последовательность байтов. Поэтому такие устройства как клавиатура, дисплей, принтер можно считать частными случаями файлов. Чтение из файла – это передача данных с внешнего устройства в оперативную память. Запись в файл – это передача данных из оперативной памяти на внешнее устройство.

Работа с файлами в C# осуществляется при помощи потоков (stream). Поток - это абстрактное понятие, используемое для обозначения потока байтов, передаваемого от источника к приемнику. Поток не зависит от конкретного устройства, с которым производится обмен данными (файл на диске, клавиатура, дисплей, оперативная память, принтер и т.п.).

Для повышения скорости передачи данных обмен с потоком обычно производится специальную область оперативной памяти – буфер. При записи в файл вся информация сначала направляется в буфер. Передача данных из буфера на внешнее устройство происходит либо при заполнении буфера, либо после специальной команды сброса. При чтении из файла данные также сначала считываются в буфер до тех пор пока буфер не будет заполнен. Буфер выделяется для каждого открытого файла.

В наиболее распространенных языках программирования работа с файлами реализована, как правило, двумя способами: в одних внутренними средствами компилятора, интерпретатора, в других с помощью функций и классов подключаемых библиотек.

В C# для работы с потоками используется обширная библиотека классов, определенных в пространстве имен System.IO.

Программа на C# может работать с потоками нескольких типов: стандартные потоки, потоки локальных файлов и потоки, связанные с оперативной памятью.

Рассмотрим основные классы иерархии для работы с потоками.

FileStream – обеспечиваетпроизвольныйдоступ к файлу как к потоку байтов. (Имеет метод seek, чтобы переходить на определенную позицию в потоке данных для их обработки).

StreamWriter – обеспечиваетпоследовательнуюзапись в файл текстовой информации

StreamReder - обеспечиваетпоследовательноечтение из файла текстовой информации

BinaryReader– предназначен для побайтового чтения из файла встроенных типов данных в двоичном виде

BinaryWriter– предназначен для побайтовой записи в файл встроенных типов данных в двоичном виде

Directory, DirectoryInfo, File, FileInfо— обеспечивают работу с каталогами и файлами (создание, удаление, получение свойств).

Существуют и другие классы для работы с потоками, с которыми вы можете ознакомиться в [].

Для работы с файлом в языке C# необходимо выполнить следующую последовательность действий

  1. Подключить пространство имен System.IO.

  2. Создать поток (объект соответствующего класса из пространства имен System.IO) и связать его с физическим файлом.

  3. Операцию создания потока заключить в блок tryи написать соответствующие обработчики исключений.

  4. Осуществить с файлом необходимые операции.

  5. Закрыть файл (отсоединить физический файл от потока).

Рассмотрим, как это последовательность операторов будет выглядеть для потока типа FileStream. Подчеркнем, что именно этот класс обеспечивает произвольный доступ к файлу как потоку байтов, т.е. позволяет считать или записать произвольный элемент данных по произвольному адресу. Прямой доступ обеспечивает высокую скорость получения информации.

Для создания потока используется один из конструкторов класса. На практике чаще применяется конструктор с тремя параметрами.

Например,

FileStream f=new FileStream(“1.txt”, FileMode.Create, FileAccess.ReadWrite);

Первый параметр конструктора - имя присоединяемого физического файла.

Второй параметр конструктора – режим открытия файла, задаваемый константой, описанной в перечисленииFileMode:

Append– открыть файл для дозаписи (указатель устанавливается в конец файл) или создать, если он не существет;

Create– создать новый файл (если существует одноименный файл, он будет удален)

CreateNew- создать новый файл (если существует одноименный файл, возникнет исключениеIOException)

Open– открыть существующий файл (указатель устанавливается в начало файла).

OpenOrCreate– открыть файл, или создать, если он не существует.

Третий параметр конструктора – режим доступа к файлу, задаваемый константой, описанной в перечисленииFileAccess:

Read– открыть файл для чтения;

ReadWrite– открыть файл для чтения и записи;

Write– открыть файл только для записи.

При создании потока может генерироваться ряд исключений, например:

FileNotFoundException– файлcуказанным именем не существует

DirectoryNotFoundException– каталог с указанным именем не существует

ArgumentException– неверно задан режим открытия файла

Для чтения и записи данных в файлиспользуются методы классаFileStream:

Read, ReadByte и Write, WriteByte соответственно. МетодыRead,Writeиспользуются для работы последовательностью байтов, аReadByte,WriteByteс одним байтом.

Например,

f.WriteByte(20); //запись в файл числа 20

byte[]a=newbyte[5](1,2,3,4,5);

f.Write(a,1,4); //запись в файл 4 элементов из массива, начиная с индекса 1.

Кроме того на практике часто используются следующие методы класса FileStream:

Length– возвратить длину потока в байтах,

Seek– установить текущий указатель на заданную позицию

Position– возвратить текущую позицию в потоке.

Например,

f.Seek(0, SeekOrigin.Begin); //сдвинуть текущий указатель на 0 байтов от начала файла;

Для закрытия файла используется метод Close().

Например, f.Close();

При закрытии файл освобождаются все связанные с ним ресурсы, например для файла, открытого для записи, в файл выгружается содержимое буфера. Поэтому всегда рекомендуется закрывать файл после окончания работы с ним. Если необходимо выгрузить буфер без закрытия файла, используют метод Flush.

Рассмотрим пример работы с файлми.

static void Main()

{

try

{

FileStream f = new FileStream("1.txt", FileMode.Create, FileAccess.ReadWrite);

f.WriteByte(20); //запись в файл числа 20

byte[] a = new byte[5]{1, 2, 3, 4, 5};

f.Write(a, 1, 4); //запись в файл 4 элементов из массива, начиная с индекса 1.

long len = f.Length;

Console.WriteLine("Длина потока в байтах " + len);

Console.WriteLine("Текущая позиция в файле "+f.Position);

f.Seek(0, SeekOrigin.Begin);

Console.WriteLine("Текущая позиция в файле " + f.Position);

byte[] b = new byte[len];

f.Read(b, 0, (int)len);

for (int i=0; i<len; i++) Console.Write (Console.Write(b[i]+" ");

f.Close();

}

catch (FileNotFoundException e)

{ Console.WriteLine(e.Message); Console.WriteLine("Проверьте правильность имени файла"); }

catch (Exception e)

{ Console.WriteLine("Error: "+e.Message); }

Console.ReadKey();

}

Результат работы программы

Длина потока в байтах 5

Текущая позиция в файле 5

Текущая позиция в файле 0

20 2 3 4 5

Для работы с текстом, в задачах где не требуется прямого доступа к данным, удобно использовать потоки StreamWriter и StreamReder. Эти потоки автоматически выполняют перекодирование из байтов в символы с кодировкой Unicode и обратно. При этом осуществляется только последовательный доступ к файлу.

При создании объектов данных поток обычно используется конструктор с одним параметром – именем файл, например,

StreamWriter f = new StreamWriter("1.txt");

Для класса StreamWriter чаще всего используются методы Close, Flush, аналогичные одноименным методам класса FileStream, а также метод Write и WriteLine, используемые Вами при работе с классом Console.

Для класса StreamReaderкроме известных Вам методовReadиReadLine, используются следующие методы:

Peek– возвращающий следующий символ без изменения позиции указателя в файле;

ReadBlock– считывающий из входного потока указанное количество символов и записывающий их в буфер;

ReadToEnd– считывающий все символы до конца потока в строку типа string, начиная с текущей позиции.

Как известно, метод ReadLineпозволят считывать только строки типаstring.Метод ReadLineвозвращаетnull, если достигнут конец файла.

Если из текстового файла требуется считать числа, разделенные, например, пробелами, то посыпают следующим образом:

  • содержимое файла f считывают в объект типа string, например string s = f.ReadLine(); (если требуется считать строку) или s= f.ReadToEnd(); (если требуется считать все данные из файла);

  • с помощью метода Split класса string разбивают считанную строку на подстроки, например, string [] buf=s.Split(‘ ’);

  • каждую из выделенных подстрок buf[i] преобразует в число соответствующего типа с использованием методов (ToInt32, ToDouble и т.д.) класса Convert и один из его методов или метода Parse, имеющий в каждом арифметическом классе.

Рассмотрим пример программы, которая записывает из заданных массивов в текстовый файл в отдельных строках информацию об учениках. В каждой строке файла должна содержаться фамилия ученика, средняя оценка (в формате два знака после запятой), номер курса. Эти данные разделены пробелами. Затем программа считывает эти данные из файла и выводит на экран, увеличивая оценку на 0,1 и курс обучения на 1.

static void Main()

{

try

{

StreamWriter f = new StreamWriter("1.txt");//выходной поток

string[] s = new string[3] { "Иванов", "Петров", "Сидоров" };

float[] m = new float[3] { 3.446F, 4.3F, 5.1111F };

int[] y = new int[3] { 1, 2, 3 };

for (int i = 0; i < s.Length; i++)

f.WriteLine("{0} {1:F2} {2}",s[i], m[i], y[i]);// запись в файл

f.Close();

StreamReader f1 = new StreamReader("1.txt");//входной поток

string st;

string[] buf;

while ((st = f1.ReadLine()) != null) // чтение из файла

{

buf = st.Split(' ');

Console.Write(buf[0] + " " );

Console.Write(Convert.ToDouble(buf[1]) + 0.1);

Console.Write( " ");

Console.WriteLine(Convert.ToInt32(buf[2]) + 1);

}

f1.Close();

}

catch (FileNotFoundException e)

{ Console.WriteLine(e.Message); Console.WriteLine("Проверьте правильность имени файла"); }

catch (Exception e)

{ Console.WriteLine("Error: " + e.Message); }

Console.ReadKey();