Бинарные файлы. BinaryWriter и BinaryReader

Последнее обновление: 20.02.2022

Для работы с бинарными файлами предназначена пара классов BinaryWriter и BinaryReader. Эти классы позволяют читать и записывать данные в двоичном формате.

BinaryWriter

Для создания объекта BinaryWriter можно применять ряд конструкторов. Возьмем наиболее простую:

BinaryWriter(Stream stream)

в его конструктор передается объект Stream (обычно это объект FileStream).

Основные методы класса BinaryWriter

  • Close(): закрывает поток и освобождает ресурсы

  • Flush(): очищает буфер, дописывая из него оставшиеся данные в файл

  • Seek(): устанавливает позицию в потоке

  • Write(): записывает данные в поток. В качестве параметра этот метод может принимать значения примитивных данных:

    • Write(bool)

    • Write(byte)

    • Write(char)

    • Write(decimal)

    • Write(double)

    • Write(Half)

    • Write(short)

    • Write(int)

    • Write(long)

    • Write(sbyte)

    • Write(float)

    • Write(string)

    • Write(ushort)

    • Write(uint)

    • Write(ulong)

    Либо можно передать массивы типов byte и char

    • Write(byte[])

    • Write(char[])

    • Write(ReadOnlySpan<byte>)

    • Write(ReadOnlySpan<char>)

    При записи массива дополнительно можно указать, с кого элемента массива надо выполнять запись, а также число записываемых элементов массива:

    • Write(byte[], int, int)

    • Write(char[], int, int)

Рассмотрим простейшую запись бинарного файла:

string path = "person.dat";

// создаем объект BinaryWriter
using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.OpenOrCreate)))
{
    // записываем в файл строку
    writer.Write("Tom");
    // записываем в файл число int
    writer.Write(37);
    Console.WriteLine("File has been written");
}

Здесь в файл person.dat записываются два значения: строка "Tom" и число 37. Для создание объекта применяется вызов new BinaryWriter(File.Open(path, FileMode.OpenOrCreate))

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

string path = "people.dat";

// массив для записи
Person[] people =
{
    new Person("Tom", 37),
    new Person("Bob", 41)
};

using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.OpenOrCreate)))
{
    // записываем в файл значение каждого свойства объекта
    foreach (Person person in people)
    {
        writer.Write(person.Name);
        writer.Write(person.Age);
    }
    Console.WriteLine("File has been written");
}

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

В данном случае последовательно сохраняем в файл people.dat данные объектов Person из массива people.

BinaryReader

Для создания объекта BinaryReader можно применять ряд конструкторов. Возьмем наиболее простую версию:

Reader(Stream stream)

в его конструктор также передается объект Stream (также обычно это объект FileStream).

Основные методы класса BinaryReader

  • Close(): закрывает поток и освобождает ресурсы

  • ReadBoolean(): считывает значение bool и перемещает указатель на один байт

  • ReadByte(): считывает один байт и перемещает указатель на один байт

  • ReadChar(): считывает значение char, то есть один символ, и перемещает указатель на столько байтов, сколько занимает символ в текущей кодировке

  • ReadDecimal(): считывает значение decimal и перемещает указатель на 16 байт

  • ReadDouble(): считывает значение double и перемещает указатель на 8 байт

  • ReadInt16(): считывает значение short и перемещает указатель на 2 байта

  • ReadInt32(): считывает значение int и перемещает указатель на 4 байта

  • ReadInt64(): считывает значение long и перемещает указатель на 8 байт

  • ReadSingle(): считывает значение float и перемещает указатель на 4 байта

  • ReadString(): считывает значение string. Каждая строка предваряется значением длины строки, которое представляет 7-битное целое число

С чтением бинарных данных все просто: соответствующий метод считывает данные определенного типа и перемещает указатель на размер этого типа в байтах, например, значение типа int занимает 4 байта, поэтому BinaryReader считает 4 байта и переместит указатель на эти 4 байта.

Например, выше в примере с BinaryWriter в файл person.dat записывалась строка и число. Считаем их с помощью BinaryReader:

using (BinaryReader reader = new BinaryReader(File.Open("person.dat", FileMode.Open)))
{
    // считываем из файла строку
    string name = reader.ReadString();
    // считываем из файла число 
    int age = reader.ReadInt32();
    Console.WriteLine($"Name: {name}  Age: {age}");
}

Конструктор класса BinaryReader также в качестве параметра принимает объект потока, только в данном случае устанавливаем в качестве режима FileMode.Open: new BinaryReader(File.Open("person.dat", FileMode.Open)).

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

Или подобным образом считаем данные из файла people.dat, который был записан в примере выше и который содержит данные объектов Person:

// список для считываемых данных
List<Person> people = new List<Person>();

// создаем объект BinaryWriter
using (BinaryReader reader = new BinaryReader(File.Open("people.dat", FileMode.Open)))
{
    // пока не достигнут конец файла
    // считываем каждое значение из файла
    while (reader.PeekChar() > -1)
    {
        string name = reader.ReadString();
        int age = reader.ReadInt32();
        // по считанным данным создаем объект Person и добавляем в список
        people.Add(new Person(name, age));
    }
}
// выводим содержимое списка people на консоль
foreach(Person person in people)
{
    Console.WriteLine($"Name: {person.Name}  Age: {person.Age}");
}

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Здесь в цикле while считываем данные. Чтобы узнать окончание потока, вызываем метод PeekChar(). Этот метод считывает следующий символ и возвращает его числовое представление. Если символ отсутствует, то метод возвращает -1, что будет означать, что мы достигли конца файла.

В цикле последовательно считываем значения для свойств объектов Person в том же порядке, в каком они записывались.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850