Полное руководство. С# 4.0
Шрифт:
В приведенном ниже примере программы демонстрируется ввод-вывод в файл с произвольным доступом. Сначала в файл записываются прописные буквы английско го алфавита, а затем его содержимое считывается обратно в произвольном порядке. // Продемонстрировать произвольный доступ к файлу. using System; using System.IO; class RandomAccessDemo { static void Main { FileStream f = null; char ch; try { f = new FileStream("random.dat", FileMode.Create); // Записать английский алфавит в файл. for (int i=0; i < 26; i++) f.WriteByte((byte)('A'+i)); // А теперь считать отдельные буквы английского алфавита. f.Seek(0, SeekOrigin.Begin); // найти первый байт ch = (char) f.ReadByte; Console.WriteLine("Первая буква: " + ch); f.Seek(1, SeekOrigin.Begin); // найти второй байт ch = (char) f.ReadByte; Console.WriteLine("Вторая буква: " + ch); f.Seek(4, SeekOrigin.Begin); // найти пятый байт ch = (char) f.ReadByte; Console.WriteLine("Пятая буква: " + ch); Console.WriteLine ; // А теперь прочитать буквы английского
При выполнении этой программы получается следующий результат. Первая буква: А Вторая буква: В Пятая буква: Е Буквы алфавита через одну: А C E G I K M O Q S U W Y
Несмотря на то что метод Seek имеет немало преимуществ при использовании с файлами, существует и другой способ установки текущего положения в файле с по мощью свойства Position. Как следует из табл. 14.2, свойство Position доступно как для чтения, так и для записи. Поэтому с его помощью можно получить или же установить текущее положение в файле. В качестве примера ниже приведен фрагмент кода из предыдущей программы записи и чтения из файла с произвольным досту пом random.dat, измененный с целью продемонстрировать применение свойства Position. Console.WriteLine("Буквы алфавита через одну: "); for(int i=0; i < 26; i += 2) { f.Position = i; // найти i-й символ посредством свойства Position ch = (char) f.ReadByte; Console.Write(ch + " "); } Применение класса MemoryStream
Иногда оказывается полезно читать вводимые данные из массива или записывать выводимые данные в массив, а не вводить их непосредственно из устройства или вы водить прямо на него. Для этой цели служит класс MemoryStream. Он представляет собой реализацию класса Stream, в которой массив байтов используется для ввода и вывода. В классе MemoryStream определено несколько конструкторов. Ниже пред ставлен один из них: MemoryStream(byte[] buffer)
где buffer обозначает массив байтов, используемый в качестве источника или адре сата в запросах ввода-вывода. Используя этот конструктор, следует иметь в виду, что массив buffer должен быть достаточно большим для хранения направляемых в него данных.
В качестве примера ниже приведена программа, демонстрирующая применение класса MemoryStream в операциях ввода-вывода. // Продемонстрировать применение класса MemoryStream. using System; using System.IO; class MemStrDemo { static void Main { byte[] storage = new byte[255]; // Создать запоминающий поток. MemoryStream memstrm = new MemoryStream(storage); // Заключить объект memstrm в оболочки классов // чтения и записи данных в потоки. StreamWriter memwtr = new StreamWriter(memstrm); StreamReader memrdr = new StreamReader(memstrm); try { // Записать данные в память, используя объект memwtr. for(int i=0; i < 10; i++) memwtr.WriteLine("byte [" + i + "]: " + i); // Поставить в конце точку. memwtr.WriteLine("."); memwtr.Flush; Console.WriteLine("Чтение прямо из массива storage: "); // Отобразить содержимое массива storage непосредственно, foreach(char ch in storage) { if (ch == '.') break; Console.Write(ch); } Console.WriteLine("\nЧтение из потока с помощью объекта memrdr: "); // Читать из объекта memstrm средствами ввода данных из потока. memstrm.Seek(0, SeekOrigin.Begin); // установить указатель файла // в исходное положение string str = memrdr.ReadLine; while(str != null) { str = memrdr .ReadLine; if (str[0] == '.') break; Console.WriteLine(str); } } catch(IOException exc) { Console.WriteLine("Ошибка ввода-вывода\n" + exc.Message); } finally { // Освободить ресурсы считывающего и записывающего потоков. memwtr.Close; memrdr.Close; } } }
Вот к какому результату приводит выполнение этой программы. Чтение прямо из массива storage: byte [0]: 0 byte [1]: 1 byte [2]: 2 byte [3]: 3 byte [4]: 4 byte [5]: 5 byte [6]: 6 byte [7]: 7 byte [8]: 8 byte [9]: 9 Чтение из потока с помощью объекта memrdr: byte [1]: 1 byte [2]: 2 byte [3]: 3 byte [4]: 4 byte [5]: 5 byte [6]: 6 byte [7]: 7 byte [8]: 8 byte [9]: 9
В этой программе сначала создается массив байтов, называемый storage. Затем этот массив используется в качестве основной памяти для объекта memstrm класса MemoryStream. Из объекта memstrm, в свою очередь, создаются объекты memrdr клас са StreamReader и memwtr класса StreamWriter. С помощью объекта memwtr выво димые данные записываются в запоминающий поток. Обратите внимание на то, что после записи выводимых данных для объекта memwtr вызывается метод Flush. Это необходимо для того, чтобы содержимое буфера этого объекта записывалось непо средственно в базовый массив. Далее содержимое базового массива байтов отобража ется вручную в цикле foreach. После этого указатель файла устанавливается с по мощью метода Seek в начало запоминающего потока, из которого затем вводятся данные с помощью объекта потока memrdr.
Запоминающие потоки очень полезны для программирования.
С их помощью можно, например, организовать сложный вывод с предварительным накоплением данных в массиве до тех пор, пока они не понадобятся. Этот прием особенно поле зен для программирования в такой среде с графическим пользовательским интер фейсом, как Windows. Кроме того, стандартный поток может быть переадресован из массива. Это может пригодиться, например, для подачи тестовой информации в программу. Применение классов StringReader и StringWriterДля выполнения операций ввода-вывода с запоминанием в некоторых приложе ниях в качестве базовой памяти иногда лучше использовать массив типа string, чем массив типа byte. Именно для таких случаев и предусмотрены классы StringReader и StringWriter. В частности, класс StringReader наследует от класса TextReader, а класс StringWriter — от класса TextWriter. Следовательно, они представля ют собой потоки, имеющие доступ к методам, определенным в этих двух базовых классах, что позволяет, например, вызывать метод ReadLine для объекта класса StringReader, а метод WriteLine — для объекта класса StringWriter.
Ниже приведен конструктор класса StringReader: StringReader(string s)
где s обозначает символьную строку, из которой производится чтение.
В классе StringWriter определено несколько конструкторов. Ниже представлен один из наиболее часто используемых. StringWriter
Этот конструктор создает записывающий поток, который помещает выводимые данные в строку. Для получения содержимого этой строки достаточно вызвать метод ToString.
Ниже приведен пример, демонстрирующий применение классов StringReader и StringWriter. // // Продемонстрировать применение классов StringReader и StringWriter. using System; using System.IO; class StrRdrWtrDemo { static void Main { StringWriter strwtr = null; StringReader strrdr = null; try { // Создать объект класса StringWriter. strwtr = new StringWriter; // Вывести данные в записывающий поток типа StringWriter. for (int i=0; i < 10; i++) strwtr.WriteLine("Значение i равно: " + i); // Создать объект класса StringReader. strrdr = new StringReader(strwtr.ToString); //А теперь ввести данные из считывающего потока типа StringReader. string str = strrdr.ReadLine; while(str != null) { str = strrdr.ReadLine; Console.WriteLine(str); } } catch(IOException exc) { Console.WriteLine("Ошибка ввода-вывода\n" + exc.Message); } finally { // Освободить ресурсы считывающего и записывающего потоков. if(strrdr != null) strrdr.Close; if(strwtr != null) strwtr.Close; } } }
Вот к каком результату приводит выполнение этого кода. Значение i равно: 1 Значение i равно: 2 Значение i равно: 3 Значение i равно: 4 Значение i равно: 5 Значение i равно: 6 Значение i равно: 7 Значение i равно: 8 Значение i равно: 9
В данном примере сначала создается объект strwtr класса StringWriter, в кото рый выводятся данные с помощью метода WriteLine. Затем создается объект класса StringReader с использованием символьной строки, содержащейся в объекте strwtr. Эта строка получается в результате вызова метода ToString для объекта strwtr. И наконец, содержимое данной строки считывается с помощью метода ReadLine. Класс File
В среде .NET Framework определен класс File, который может оказаться полезным для работы с файлами, поскольку он содержит несколько статических методов, выпол няющих типичные операции над файлами. В частности, в классе File имеются методы для копирования и перемещения, шифрования и расшифровывания, удаления фай лов, а также для получения и задания информации о файлах, включая сведения об их существовании, времени создания, последнего доступа и различные атрибуты файлов (только для чтения, скрытых и пр.). Кроме того, в классе File имеется ряд удобных ме тодов для чтения из файлов и записи в них, открытия файла и получения ссылки типа FileStream на него. В классе File содержится слишком много методов для подроб ного их рассмотрения, поэтому мы уделим внимание только трем из них. Сначала бу дет представлен метод Сору, а затем — методы Exists и GetLastAccessTime. На примере этих методов вы сможете получить ясное представление о том, насколь ко удобны методы, доступные в классе File. И тогда вам станет ясно, что класс File определенно заслуживает более тщательного изучения.
СОВЕТ Ряд методов для работы с файлами определен также в классе FileInfo. Этот класс отли чается от класса File одним, очень важным преимуществом: для операций над файлами он пре доставляет методы экземпляра и свойства, а не статические методы. Поэтому для выполнения нескольких операций над одним и тем же файлом лучше воспользоваться классом FileInfo. Копирование файлов с помощью метода Сору
Ранее в этой главе демонстрировался пример программы, в которой файл копиро вался вручную путем чтения байтов из одного файла и записи в другой. И хотя задача копирования файлов не представляет особых трудностей, ее можно полностью авто матизировать с помощью метода Сору, определенного в классе File. Ниже пред ставлены две формы его объявления. static void Copy (string имя_исходного_файла, string имя_целевого_файла) static void Copy (string имя_исходного_файла, string имя_целевого_файла, boolean overwrite)