Чтение запись структурированного двоичного файла

#c# #.net #bytebuffer #afp

#c# #.net #bytebuffer #afp

Вопрос:

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

введите описание изображения здесь

Файл состоит из «ЗАПИСЕЙ». Каждая «ЗАПИСЬ» имеет следующую структуру: я буду использовать первую запись в качестве примера

  • (красный) НАЧАЛЬНЫЙ байт: 0x5A (всегда 1 байт, фиксированное значение 0x5A)
  • (зеленый) ДЛИНА в байтах: 0x00 0x16 (всегда 2 байта, значение может измениться с «0x00 0x02» на «0xFF 0xFF»)
  • (синий) СОДЕРЖИМОЕ: количество байтов, указанное десятичным значением поля ДЛИНЫ минус 2. В этом случае значение поля ДЛИНЫ равно 22 (0x00 0x16, преобразованное в десятичное число), поэтому содержимое будет содержать 20 (22 — 2) байта.

Моя цель — прочитать каждую запись одну за другой и записать ее в выходной файл. На самом деле у меня есть функция чтения и функция записи (некоторый псевдокод):

 private void Read(BinaryReader binaryReader, BinaryWriter binaryWriter)
{
    byte START = 0x5A;
    int decimalLenght = 0;
    byte[] content = null;
    byte[] length = new byte[2];

    while (binaryReader.PeekChar() != -1)
    {
        //Check the first byte which should be equals to 0x5A
        if (binaryReader.ReadByte() != START)
        {
            throw new Exception("0x5A Expected");
        }

        //Extract the length field value
        length = binaryReader.ReadBytes(2);

        //Convert the length field to decimal
        int decimalLenght = GetLength(length);

        //Extract the content field value
        content = binaryReader.ReadBytes(decimalLenght - 2);

        //DO WORK
        //modifying the content

        //Writing the record
        Write(binaryWriter, content, length, START);
    }
}

private void Write(BinaryWriter binaryWriter, byte[] content, byte[] length, byte START)
{
    binaryWriter.Write(START);
    binaryWriter.Write(length);
    binaryWriter.Write(content);   
}
  

Этот способ действительно работает.
Однако, поскольку я имею дело с очень большими файлами, я нахожу, что он вообще не работает, потому что я читаю и записываю 3 раза для каждой записи. На самом деле я хотел бы читать фрагменты данных с ошибками вместо небольшого количества байтов и, возможно, работать в памяти, но мой опыт использования Stream останавливается с BinaryReader и BinaryWriter. Заранее спасибо.

Ответ №1:

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

Вы говорите, что это «вообще не выполняется» — насколько быстро это работает? Насколько вы уверены, что ваше время уходит на ввод-вывод? Выполняли ли вы какое-либо профилирование кода?

Комментарии:

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

2. @JamesJohnston: Действительно. Я бы ожидал, что значения по умолчанию будут подходящими для большинства сценариев. Я сомневаюсь, что это действительно проблема.

3. Я согласен. Я столкнулся с одним исключением, хотя и не с C #. Среда выполнения C, которую я использовал, по умолчанию имела буфер размером 512 байт. Таким образом, производительность диска была бы чрезвычайно низкой при чтении файла данных 4 байта за раз (что и сделала наша программа). Конечно, обычно это не проблема, потому что буферы операционной системы считывают, даже если программа не выполняет адекватную работу.

4. Однако сетевые диски представляют особую ситуацию. Упреждающее чтение может быть опасным: другой клиент может записывать, делая недействительным буфер клиента. Windows использует оппортунистическую блокировку (oplocks) для безопасного выполнения буферизации с опережением чтения по сети. Короче говоря, некоторые устаревшие файловые системы (Paradox / MS Access / QuickBooks / и т.д.) могут испытывать серьезное повреждение файлов при потере сетевого соединения. Эти люди могут отключить блокировку для сохранения целостности базы данных. Когда это произошло, наша производительность чтения снизилась, потому что использовался только буфер C runtime 512 байт.

5. Однако, если операционная система не работает на сетевом диске с отключенными блокировками, то проблема, скорее всего, в другом месте, и операционной системе, вероятно, потребуется больше профилировать код.

Ответ №2:

Я мог бы также предложить вам сначала прочитать 3 (или 6?) Байта вместо 2 отдельных операций чтения. Поместите начальные байты в небольшой массив, проверьте 5a ck-байт, затем 2-байтовый индикатор длины, затем 3-байтовый код операции AFP, ЗАТЕМ прочитайте оставшуюся часть записи AFP.

Это небольшая разница, но она избавляет от одного из ваших вызовов read.

Я не Джон Скит, но я довольно долго работал в одном из крупнейших магазинов печати и почты в стране, и мы в основном выводили AFP 🙂

(хотя обычно на C)