Смекни!
smekni.com

Основы алгоритмизации и программирования 2 (стр. 29 из 32)

Оба класса File и FileInfo предоставляют методы OpenRead() и OpenWrite(), которые облегчают создание объектов FileStream. Первый открывает метод с режимом доступа "только чтение", а второй, кроме того, разрешает осуществлять запись. Это позволяет упростить обращение к файлу, так что вам теперь не придется указывать всю информацию, как в предыдущих примерах. Например, следующая строка открывает файл Data.txt в режиме "только чтение":

FileStream aFile = File.OpenRead("Data.txt");

Обратите внимание на то, что следующий код выполняет те же самые действия:

FileInfo aFile = new FileInfo("Data.txt") ;
FileStream aFile = aFile OpenRead() ;

8.1.7.Позиция внутри файла

Класс FileStream поддерживает внутренний указатель файла, ссылающийся на то место в файле, в котором будет производиться очередная операция чтения или записи. В большинстве случаев при открытии файла указатель устанавливается в начало файла, однако такое поведение указателя может быть изменено, что позволяет приложению осуществлять чтение или запись в любой точке файла. Можно также организовать произвольный доступ к файлу и осуществлять поиск конкретной позиции в файле. Такой подход позволяет сэкономить массу времени при работе с очень большими файлами, поскольку можно постоянно отыскивать необходимую позицию.

Метод, который реализует данную функциональную возможность, носит название Seek ():

public long Seek(long offset, SeekOrigin origin);

Первый параметр определяет, насколько далеко вперед в байтах должен быть передвинут указатель. Второй параметр определяет, с какой точки вести отсчет. Перечислимый тип SeekOrigin состоит из трех значений: Begin (начало), current (текущая позиция) и End (конец). Следующая строка передвигает указатель файла на восьмой байт, считая с самого первого байта данного файла:

aFile.Seek(8, SeekOrigin.Begin);

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

aFile.Seek(2, SeekOrigin.Current);

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

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

aFile.Seek(-5, SeekOrigin.End);

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

8.1.8.Чтение данных

Чтение данных с использованием класса FileStream оказывается не таким простым, как с использованием классов StreamReader и StreamWriter, к рассмотрению которых мы вернемся ниже в данной главе. Это происходит потому, что класс FileStream имеет дело исключительно с байтами, что делает класс полезным при работе с файлами произвольного типа, а не только с текстовыми файлами. Побайтное считывание данных позволяет использовать этот класс при работе с графическими и звуковыми файлами. Однако платой за подобную гибкость является невозможность с помощью класса FileStream считывать данные непосредственно в переменные типа строки. Это позволяют осуществлять классы StreamWriter и StreamReader. Существует, однако, несколько конверсионных классов, которые существенно облегчают выполнение преобразования массива байтов в символьные массивы и обратно.

Метод FileStream.Read() является основным средством, позволяющим осуществлять доступ к данным, содержащимся в файле, на который ссылается объект FiieStream:

public int Read(byte[] array, int offset, int count);

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

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

которого будет производиться чтение, используется файл, специально созданный для данного примера. ПРАКТИКУМ. Чтение данных из файла с произвольным доступом

1. Откройте Visual Studio.NET.

2. Откройте новый проект, выбрав File | New j Project. Выберите пункт С# Console Application и назовите его ReadFile.

3. Добавьте два следующих объявления пространств имен в самое начало файла Class1.cs. Пространство имен System.IO необходимо для класса FiieStream, а пространство Имен System.Text потребуется для осуществления преобразования, которое нам придется выполнять над байтами, считанными из файла.

using System; using System.IO; using System.Text;

4. Добавьте следующий код в метод Main():

static void Main(string[] args)

{

byte[] byData = new byte[100]; char[] charData = new Char[100]; try { FileStream aFile = new FileStream("../../Classl.cs",FileMode.Open); aFile. Seek (55, SeekOngin.Begin) ; aFile.Read(byData,0,100); }

catch(IOException e) {

Console.WriteLine("An IO exception has been thrown!");

Console.WriteLine{e.ToString ()) ;

Console.ReadLine(); return; }

Decoder d = Encoding. UTF8.GetDecoder();

d.GetChars(byData, 0, byData.Length, charData, 0);

Console.WriteLine(charData); Console.ReadLine(); return;

}

5. Запустите приложение. Нажмите клавишу Enter для закрытия консоли

Как это работает

Чтение данных из файла с произвольным доступом

Данное приложение открывает свой собственный файл с расширением .cs, из которого затем осуществляется чтение.

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

FileStream aFile = new FileStream(" ./. ./Classl.cs", FileMode.Open) ;

Две строки кода, которые в действительности осуществляют поиск по файлу и считывание из него, имеют следующий вид: try { aFile.Seek(55,SeekOrigin.Begin); aFile. Read (byData, 0,100); }

catch(IOException e) {

Console.WriteLine("An 10 exception has been thrown!");

Console.WriteLine(e.ToString());

Console.ReadLine(); return; }

В результате выполнения первой из них указатель файла перемещается на 55-й байт файла. Это символ 'n' из слова namespace; 54 предыдущих символа находятся в строках с директивами using. В результате выполнения второй строки следующие 100 байтов считываются в байтовый массив byData.

Обратите внимание на то, что нами используется конструкция для обработки исключительных ситуаций try-catch. Практически любая операция, связанная с вводом/выводом, может привести к возникновению исключительной ситуации типа IOException. Любой исполнимый код должен включать в себя обработку исключительных ситуаций, особенно если в нем ведется работа с файловой системой.

После того как информация считана из файла в байтовый массив, возникает необходимость преобразовать ее в символьный массив с тем, чтобы получить возможность выводить ее на консоль. С этой целью воспользуемся классом Decoder из пространства имен System.Text. Этот класс предназначен для того, чтобы преобразовывать исходные байты в какие-либо более полезные объекты, например, в символы:

Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0)

;

В этих строках осуществляется создание объекта Decoder, который основывается на кодировке UTF8. Это схема кодирования Unicode. После этого вызывается метод GetChars (), который принимает массив байтов и преобразовывает его в массив символов. После завершения этой процедуры массив символов оказывается готовым к выводу на консоль.

8.1.9.Запись данных

Процесс записи данных в файл с произвольным доступом совершенно аналогичен. Сначала необходимо создать байтовый массив; наиболее простой способ достигнуть этого – создать сначала символьный массив, который мы собираемся записывать в файл. Затем следует воспользоваться объектом Encoder, позволяющим преобразовать его в массив байтов; использование этого объекта очень напоминает использование объекта Decoder. В завершение необходимо вызывать метод Write (), который и запишет информацию в файл.

Давайте создадим простой пример, который позволит продемонстрировать, каким образом это выполняется.

ПРАКТИКУМ. Запись данных в файл с произвольным доступом

1. Создайте новый проект. Выберите пункт меню С# Console Application и назовите его WriteFile.

2. Как и в предыдущем случае, вставьте в начало файла Class1.cs следующие два описания пространств имен: using System; using System.IO; using System.Text;

3. Добавьте в метод Main() следующий код:

static void Main(string[] args) { byte[] byData = new byte[100]; char[] charData = new Char [100] ; try {

FileStream aFile = new FileStream("Temp. txt" . FileMode.OpenOrCreate) ; charData = "Hello World".ToCharArray(); Encoder e = Encoding.UTF8.GetEncoder();

e.GetBytes (charData, 0,charData.Length, byData, 0 , true); // Перемещение указателя файла в самое начало файла aFile.Seek(0,SeekOrigin.Begin); aFile.Write(byData,0,byData.Length); }

catch(IOException ex) {

Console.WriteLine("An 10 exception has been thrown!") ;

Console.WriteLine(ex.ToString());

Console.ReadLine(); return; }

return; }

4. Запустите приложение. Оно будет выполняться некоторое время, а затем завершится.

5. Перейдите в директорию приложения – файл будет сохранен именно в ней, поскольку мы используем относительный путь. Файл будет помещен в папку WriteFile\bin\Debug. Откройте файл Temp.txt.