C # /. NET - Пользовательские форматы двоичных файлов - с чего начать?

Мне нужно иметь возможность хранить некоторые данные в настраиваемом формате двоичного файла. Я никогда раньше не создавал свой собственный формат файла. Это должен быть удобный формат для путешествий между мирами C #, Java и Ruby / Perl / Python.

Для начала файл будет состоять из записей. Поле GUID и поле пакета JSON / YAML / XML. Я не уверен, что использовать в качестве разделителей. Запятая, табуляция или перевод строки кажутся слишком хрупкими. Что делает Excel? или форматы OpenOffice до XML? Если вы используете символы ASCII 0 или 1. Не знаю, с чего начать. Есть статьи или книги по этой теме?

Этот формат файла может быть расширен позже, чтобы включить «раздел заголовка».

Примечание. Для начала я буду работать в .NET, но мне бы хотелось, чтобы этот формат был легко переносимым.

ОБНОВЛЕНИЕ:
Обработка "пакетов" может быть медленной, а навигация по формату файла - нет. Так что я думаю, что XML не обсуждается.


person BuddyJoe    schedule 27.04.2009    source источник
comment
Повторите редактирование: каков здесь вариант использования? Во многих случаях вы предпочитаете не перемещаться внутри файла, а десериализовать его в объектную модель, а затем работать с ней. Что-нибудь еще, и вы также можете использовать файл базы данных некоторого (общего) типа.   -  person Marc Gravell    schedule 28.04.2009
comment
Я должен добавить, что этот файл будет слишком большим для сериализации. Поэтому я бы никогда не захотел хранить все данные в памяти одновременно. Это может быть сериализованный список ‹SomeObject›, но мне нужен разделитель, чтобы не читать весь список за один раз.   -  person BuddyJoe    schedule 28.04.2009


Ответы (5)


Я постараюсь добавить несколько общих советов по созданию переносимого формата двоичных файлов.

Обратите внимание, что изобрести двоичный формат файла - значит задокументировать, как в нем должны идти биты и что они означают. Это не кодирование, а документация.

Теперь подсказки:

  1. Решите, что делать с порядком байтов. Хороший и простой способ - решить это раз и навсегда. При использовании на обычном ПК (то есть x86) для сохранения преобразований (производительности) предпочтительнее выбрать вариант с прямым порядком байтов.

  2. Создайте заголовок. Да, всегда иметь заголовок. Первые байты файла должны сообщить вам, с каким форматом вы возитесь.

    • Start with magic to be able to recognize your format (ASCII string will do the trick)
    • Добавить версию. Версию вашего формата файла не помешает добавить, и это позволит вам сделать обратную совместимость позже.
  3. Наконец, добавьте данные. Теперь формат данных будет конкретным, и он всегда будет соответствовать вашим конкретным потребностям. По сути, данные будут храниться в двоичном образе некоторой структуры данных. Структура данных - это то, что вам нужно придумать.

Если вам нужен произвольный доступ к вашим данным по каким-то индексам, вам подойдут B-деревья чтобы пойти, в то время как, если вам просто нужно много чисел, чтобы записать их все, а затем прочитать их все, «массив» сделает свое дело.

Кроме того, вы можете использовать TLV (тип-длина-значение) концепция прямой совместимости.

person Jan Smrčina    schedule 29.04.2015
comment
Есть ли предложения по развитию моих знаний о страницах внутри файловых форматов? Статьи или книги, которые я должен прочитать? - person BuddyJoe; 29.04.2015
comment
Когда я говорю «страницы», я имею в виду как страницы базы данных. SQLite было немного сложно следовать коду C. Может быть, примеры Java или C # я мог бы более четко проследить. - person BuddyJoe; 29.04.2015

Как насчет использования «протокольных буферов»? Разработанный как эффективный, переносимый, совместимый с версиями двоичный формат общего назначения, он предоставляет вам C ++, Java и Python в библиотека Google, а также C #, Perl, Ruby и другие в портах сообщества < / а>?

Обратите внимание, что Guid не имеет определенного типа данных, но вы можете использовать его как сообщение с (по сути) byte[].

Обычно для работы с .NET я бы рекомендовал protobuf-net (но как автор я несколько предвзято) - однако, если вы собираетесь использовать другие языки позже, вы можете добиться большего успеха (в долгосрочной перспективе), используя Jon dotnet-protobufs; это даст вам знакомый API для всех платформ (где, поскольку protobuf-net использует идиомы .NET).

person Marc Gravell    schedule 27.04.2009
comment
И Python - это один из языков, которые Google предоставляет напрямую. - person Jon Skeet; 27.04.2009
comment
Мне интересно, должен ли я взять на себя зависимость от материала буферов протокола (даже если это лицензия Apache). Или я должен просто узнать о форматах двоичных файлов из того, что делает материал Protocol Buffers. Думаю, я уже понимаю одну зависимость от Json.NET и лицензию MIT. - person BuddyJoe; 28.04.2009

Каждый из символов ASCII 0 или 1 занимает несколько бит (как и любой другой символ), поэтому, если вы храните его таким образом, ваш «двоичный» файл будет в несколько раз больше, чем должен быть. В текстовом файле нулей и единиц не совсем двоичный файл :)

Вы можете использовать BinaryWriter для непосредственной записи необработанных данных. в файловый поток. Единственное, что вам нужно выяснить, - это преобразовать ваш формат в памяти (обычно это какой-то граф объектов) в последовательность байтов, которую может использовать BinaryWriter.

Однако, если вас в первую очередь интересует переносимость, я не рекомендую использовать двоичный формат. XML как раз разработан для решения проблемы переносимости и взаимодействия. Это многословный и весомый формат файла, но это компромисс, на который вы идете, чтобы решить эти проблемы за вас. Если удобочитаемый формат отсутствует, Ответ Марка - это то, что вам нужно. Не нужно изобретать колесо портативности!

person Rex M    schedule 27.04.2009
comment
Нет необходимости жертвовать скоростью и размером, чтобы получить переносимость - см. Ответ Марка «Протоколные буферы». Вы теряете удобочитаемость (в закодированной форме - вы можете выгрузить PB в текст), и вам нужно указать структуру заранее, но вы бесплатно получаете размер, скорость и обратную / прямую совместимость. - person Jon Skeet; 27.04.2009
comment
Вы подняли хороший вопрос по поводу комментария ASCII. Как большинство людей разграничивают начало или конец строки в двоичном формате? Я знаю, что у моего GUID будет стандартная длина, но мои пакетные данные будут строковыми. Я слышал о термине строка с завершающим нулем. Это что? Показывается отсутствие у меня должной степени CS. - person BuddyJoe; 28.04.2009
comment
@Jon Skeet, это хороший аргумент. Для меня вопрос между буферами протоколов и удобочитаемым форматом, таким как XML, - это просто необходимая степень переносимости, гибкости и открытости. Мой профессиональный опыт склонялся к необходимости очень открытых форматов, поэтому я всегда рекомендую сначала что-то в стиле XML :) - person Rex M; 28.04.2009
comment
@Tundall - для строковых / массивных данных лучше всего использовать префикс данных с размером. Тогда вы можете пропустить его, если он вам не нужен. Альтернативой является использование некоторого специального маркера (например, 0, который не встречается в обычном тексте) в качестве конца, но, конечно, вы не можете использовать его в двоичных данных (например, guids), потому что 0 является совершенно допустимым и ожидаемое двоичное значение. Так что длина префикса становится лучшим вариантом. - person Marc Gravell; 28.04.2009
comment
Например (из документа о кодировании буферов протокола) - 12 07 74 65 73 74 69 6e 67 представляет поле 2 в виде строки (12) 7 байтов (07), тестирование (остальные данные в UTF8). Я не буду пытаться объяснять 12 или то, что происходит с длинными строками (которым требуется более 1 байта для определения длины), но все это четко определено. - person Marc Gravell; 28.04.2009
comment
Думаю, я проверю документ о кодировании буферов протоколов сегодня вечером. Думаю, я также собираюсь взглянуть на формат файла SQLite, если разбираюсь в нем в двоичном редакторе. - person BuddyJoe; 28.04.2009

Это зависит от типа данных, которые вы будете записывать в двоичный файл, и какова цель двоичного файла. Это объект класса или просто запись данных? Если это данные записи, я бы рекомендовал поместить их в формате xml. Таким образом вы можете включить проверку схемы, чтобы убедиться, что файл соответствует вашим стандартам. Как в java, так и в .NET есть инструменты для импорта и экспорта данных из / в формат xml.

person greektreat    schedule 27.04.2009

Предположим, ваш формат:

    struct Format
    {
        struct Header // 1
        {
            byte a;
            bool b1, b2, b3, b4, b5, b6, b7, b8;
            string name;
        }
        struct Container // 1...*
        {
            MyTypeEnum Type;
            byte[] data;
        }
    }

    enum MyTypeEnum
    {
        Sound,
        Video,
        Image
    }

Тогда у меня был бы последовательный файл с:


байт // а

байт // b

int // размер имени

char [] // имя (размер которого указан выше, помните, что char в .NET 16 бит)

int // Тип MyTypeEnum

int // размер данных

byte [] // данные (размер которых указан выше)


Затем вы можете повторять последние три строки столько, сколько захотите.

Для чтения вы используете BinaryReader, который поддерживает чтение байтов, целых чисел и серий байтов. Также есть BinaryWriter.

Кроме того, помните, что Microsoft .NET (например, на машине Windows / Intel) является прямым порядком байтов. То же самое с BinaryReader и BinaryWriter.

person tofi9    schedule 27.04.2009
comment
См. Мой другой комментарий к этой теме о размере файла. Я думаю, что понимаю BinaryReader / Writer, но позволит ли это мне просмотреть файл понемногу? Мне не нужно десериализовать все сразу, верно? - person BuddyJoe; 28.04.2009
comment
BinaryReader / BinaryWriter - это просто помощник для любого .NET Stream. Он не буферизован, поэтому вы можете просто перейти к BaseStream и найти то место, где вы хотите, чтобы BinaryReader читал или BinaryWriter записывал. FileStream поддерживает поиск вперед и назад. Таким образом, наличие индекса где-нибудь в вашем заголовке, вероятно, поможет вам прочитать только индекс, а затем перейти к позиции, которую вы хотите прочитать. - person tofi9; 28.04.2009