Произвольный доступ к файлам

Нередко бинарные файлы представляют определенную стрктуру. И, зная эту структуру, мы можем взять из файла нужную порцию информации или наоброт записать в определенном месте файла определенный набор байтов. Например, в 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[4];

fstream.Read(output, 0, output.Length);

// декодируем байты в строку

string textFromFile = Encoding.Default.GetString(output);

Console.WriteLine("Текст из файла: {0}", 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.Length];

fstream.Read(output, 0, output.Length);

// декодируем байты в строку

textFromFile = Encoding.Default.GetString(output);

Console.WriteLine("Текст из файла: {0}", textFromFile); // hello house

}

Console.Read();

}

}

StreamReader и StreamWriter

Класс StreamReader позволяет нам легко считывать весь текст или отдельные строки из текстового файла. Среди его методов можно выделить следующие:

Close: закрывает считываемый файл и освобождает все ресурсы.

Peek: возвращает следующий доступный символ, если символов больше нет, то возвращает -1.

Read: считывает и возвращает следующий символ в численном представлении. Имеет перегруженную версию:Read(char[] array, int index, int count), где array - массив, куда считываются символы, index - индекс в массиве array, начиная с которого записываются считываемые символы, и count - максимальное количество считываемых символов

ReadLine: считывает одну строку в файле.

ReadToEnd: считывает весь текст из файла.

Считаем текст из файла различными способами:

string path= @"C:\SomeDir\hta.txt";

try

{

Console.WriteLine("******считываем весь файл********");

using (StreamReader sr = new StreamReader(path))

{

Console.WriteLine(sr.ReadToEnd());

}

Console.WriteLine();

Console.WriteLine("******считываем построчно********");

using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))

{

string line;

while ((line = sr.ReadLine()) != null)

{

Console.WriteLine(line);

}

}

Console.WriteLine();

Console.WriteLine("******считываем блоками********");

using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))

{

char[] array = new char[4];

// считываем 4 символа

sr.Read(array, 0, 4);

Console.WriteLine(array);

}

}

catch (Exception e)

{

Console.WriteLine(e.Message);

}

Как и в случае с классом FileStream здесь используется конструкция using.

В первом случае мы разом считываем весь текст с помощью метода ReadToEnd().

Во втором случае считываем построчно через цикл while: while ((line = sr.ReadLine()) != null) - сначала присваиваем переменной line результат функции sr.ReadLine(), а затем проверяем, не равна ли она null. Когда объект sr дойдет до конца файла и больше строк не останется, то метод sr.ReadLine() будет возвращать null.

В третьем случае считываем в массив четыре символа.

Обратите внимание, что в последних двух случаях в конструкторе StreamReader указывалась кодировка System.Text.Encoding.Default.

Свойство Default класса Encoding получает кодировку для текущей кодовой страницы ANSI. Также через другие свойства мы можем указать другие кодировки. Если кодировка не указана, то при чтении используется UTF8. Иногда важно указывать кодировку, так как она может отличаться от UTF8, и тогда мы получим некорректный вывод.

Наши рекомендации