Каковы важные моменты при разработке (двоичного) формата файла?

При разработке формата файла для записи двоичных данных, какие атрибуты, по вашему мнению, должен иметь этот формат? На данный момент я выделил следующие важные моменты:

  • иметь несколько «магических байтов» в начале, чтобы иметь возможность распознавать файлы (в моем конкретном случае это также должно помочь отличить файлы от «устаревших» файлов)
  • иметь номер версии файла в начале, чтобы формат файла можно было изменить позже без нарушения совместимости
  • указать порядок байтов и размер всех элементов данных; или: включить некоторое пространство для описания порядка байтов/размера данных (я бы склонялся к первому)
  • возможно, зарезервировать место для дополнительных атрибутов каждого файла, которые могут понадобиться в будущем?

Что еще может быть полезно, чтобы сделать формат более перспективным и минимизировать головную боль в будущем?


person oliver    schedule 27.11.2008    source источник


Ответы (10)


Взгляните на спецификацию PNG. Этот формат имеет очень хорошее обоснование.

Также решите, что важно для вашего будущего формата: компактность, совместимость, возможность встраивать в него другие форматы (другие алгоритмы сжатия). Другим интересным примером могут служить буферы протокола Google. , где размер передаваемых данных является королем.

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

person Stepan Stolyarov    schedule 27.11.2008
comment
PNG является одним из примеров. Другие с аналогичной структурой — это IFF (формат файлов обмена, в основном используемый на Commodore Amiga) и RIFF (например, WAV или AVI). - person BlaM; 27.11.2008

Я согласен, что это хорошие идеи:

  1. Волшебные числа в начале. Практически требуется в *nix:

  2. Номер версии файла для обратной совместимости.

  3. Спецификация порядка байтов.

Но ваш четвертый излишен, потому что # 2 позволяет вам добавлять поля, если вы меняете номер версии (и пока вам не нужно прямая совместимость).

  • возможно, зарезервировать место для дополнительных атрибутов каждого файла, которые могут понадобиться в будущем?

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

В дополнение к 1-3 выше, я бы добавил это:

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

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

  • Уточните в спецификации, что это бинарный формат, т. е. разрешены все значения от 0 до 255 для всех байтов (кроме магических чисел).

И вот некоторые необязательные:

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

  • Если вы ожидаете, что эти файлы будут найдены «в дикой природе», вы можете рассмотреть возможность встраивания какой-либо подсказки, чтобы найти спецификацию. Представьте, как полезно было бы найти строку http://www.w3.org/TR/PNG/ в файле png.

person Bart    schedule 29.12.2008
comment
Почему полный диапазон 0-255 неприемлем для магических чисел, как вы заявили? И каков подходящий диапазон? Заранее спасибо. - person bazz; 20.06.2017
comment
Я считаю, что вы всегда должны хранить свои числа в форме с прямым порядком байтов в файлах, тогда вам не нужно указывать порядок байтов. В конце концов, обратный порядок байтов — это сетевой порядок байтов, и он в значительной степени применим и к файлам. - person Zoltan Tirinda; 07.02.2019

Все зависит от назначения формата, конечно.

Одним из гибких подходов является структурирование всего файла в виде триплетов TLV (Tag-Length-Value). Например, сделайте файл состоящим из записей, каждая из которых начинается с 4-байтового заголовка:

1 byte  = record type
3 bytes = record length
followed by record content

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

person atzz    schedule 27.11.2008
comment
TLV кажется везде, настолько повсюду, что я не вижу смысла везде. - person Cheery; 29.12.2008
comment
Примечание по оптимизации относительно TLV: если вы храните их в порядке тип1-длина1-значение1-тип2-длина2-значение2 и т. д., чтение всего файла (например, для поиска определенной записи) может быть неэффективным, поскольку вам придется читать каждый вся тройка TLV. Однако, если вы сохраните все кортежи типа и длины в начале (т. е. тип1-длина1-тип2-длина2-значение1-значение2), вы можете вычислить смещение для каждой части значения, не читая все предыдущие значения. Однако файловая структура усложняется. - person oliver; 19.06.2019

Еще один момент, взятый из спецификации файла .xz (http://tukaani.org/xz/xz-file-format.txt): один из первых нескольких байтов должен быть несимвольным, «чтобы приложения не могли ошибочно определить файл как текстовый файл». Обратите внимание, сколько байтов заголовка обычно проверяют редакторы и другие инструменты, но использование недвоичного байта в первых четырех или восьми байтах кажется полезным.

person oliver    schedule 15.05.2012

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

person Bork Blatt    schedule 27.11.2008

Я бы подумал об определении подструктуры, которую более высокие уровни используют для хранения данных, немного похожей на мини-файловую систему внутри файла.

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

Давайте немного конкретнее. Рассмотрим обычные способы хранения данных в памяти: как правило, они могут быть сведены либо к непрерывным расширяемым массивам/спискам, либо к графам на основе указателей/ссылок, либо к бинарным блокам данных в определенных форматах.

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

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

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

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

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

На самом деле, XML — это хорошее место для поиска долговечности формата файла. Посмотрите на его возможности пространства имен. Если вы тщательно строите свой код для чтения и записи, должна быть возможность писать приложения, сохраняющие расположение и содержимое помеченных (рекурсивно) данных, которые они не понимают, возможно, потому, что они были написаны более поздней версией того же приложения.

person Barry Kelly    schedule 27.11.2008

Убедитесь, что вы зарезервировали код тега (или, что еще лучше, зарезервировали бит в каждом теге), который определяет удаленный/свободный блок/фрагмент. Затем блоки можно удалить, просто изменив текущий код тега блока на код удаленного тега или установив удаленный бит тега. Таким образом, вам не нужно сразу полностью реструктурировать файл при удалении блока.

Резервирование бита в теге дает возможность восстановить блок (если вы оставите данные блока без изменений).

Однако в целях безопасности вы можете захотеть обнулить данные удаленного блока, в этом случае вы должны использовать специальный тег удаленного/свободного.

Соглашусь со Степаном, что надо выбирать порядок следования, но мне бы еще индикатор порядка следования в файле. Если вы используете индикатор порядка следования байтов, вы можете рассмотреть возможность использования одного из знаков порядка следования байтов UniCode также в качестве индикатор любой текстовой кодировки UniCode, используемой для любых текстовых блоков. Спецификация обычно представляет собой первые несколько байтов текстовых файлов UniCoded, поэтому, если ваша спецификация является первой записью в вашем файле, может возникнуть проблема с идентификацией некоторой утилитой вашего файла как текста UniCode (я не думаю, что это большая проблема) . Я бы рассматривал/зарезервировал спецификацию как один из ваших обычных тегов (используя либо спецификацию UTF16 при использовании 16-битных тегов, либо спецификацию UTF32 при использовании 32-битных тегов) с блоком/фрагментом нулевой длины.

См. также http://en.wikipedia.org/wiki/File_format.

person Roger Nelson    schedule 02.12.2008
comment
Вы должны хранить строки в UTF8 в файлах, тогда вам не нужно иметь дело с BOM. - person Zoltan Tirinda; 07.02.2019

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

  • Будет ли случайный или последовательный доступ нормой?
  • Как часто будут считываться данные?
  • Как часто будут записываться данные?
  • Будете ли вы записывать файл за один раз или будете замедлять его запись по мере поступления данных.
  • Файл должен быть переносимым? Не все форматы должны быть.
  • Должна ли она быть совместима с другими версиями? Возможно, достаточно обновить файл.
  • Нужно ли легко читать/писать?
  • Компромисс размера/скорости/сложности.

Большинство ответов здесь дают хорошие советы по переносимости/совместимости, поэтому я не собираюсь добавлять больше. Но обратите внимание на следующие (часто упускаемые из виду) вещи.

  • Некоторые файлы часто записываются и редко читаются (резервные копии, журналы, ...), и вы можете сосредоточиться на размере и простоте записи.
  • Преобразование порядков байтов происходит медленно (относительно), если ваш файл никогда не покидает хост или покидает его достаточно редко, поэтому преобразование является хорошим вариантом, с помощью которого вы можете значительно повысить производительность. Рассмотрите возможность написания числа, такого как 0x1234, как часть заголовка, чтобы вы могли обнаружить (и указать пользователю преобразовать), если это так.
  • Иногда легкое чтение действительно полезно. Если вы работаете с журналами или текстовыми документами, рассмотрите возможность сжатия всего за один раз, а не для каждой записи, чтобы вы могли zcat | strings файл и посмотреть, что внутри.

Есть много вещей, о которых нужно помнить, и разработка хорошего формата требует тщательного планирования и предвидения. Такие мелочи, как zcatобработка файла и получение полезной информации или небольшой прирост производительности за счет использования собственных целых чисел, могут дать вашему продукту преимущество, однако вы должны быть осторожны, чтобы не пожертвовать чем-то важным, чтобы получить его.

person Kevin Cox    schedule 04.11.2013

Я согласен с предложением atzz использовать систему значений длины тега. Для совместимости в будущем вы можете сохранить набор «указателей» на записи TLV в начале (или, может быть, тег, указатель и указать указатель на длину, значение; или, возможно, тег, длину, указатель, а затем собрать все данные вместе в другом месте?).

Итак, мой файл может выглядеть примерно так:

magic number/file id
version
tag for first data entry
pointer to first data entry --------+
tag for second data entry           |
pointer to second data entry        |
...                                 |
length of first data entry <--------+
value for first data entry
...

магический номер, версия, теги, указатели и длины будут предопределенной длиной набора для облегчения декодирования. Скажем, 2 байта. Или 4, в зависимости от того, что вам нужно. Не обязательно, чтобы все они были одинаковыми (например, все теги имеют размер 1 байт, указатели — 4 и т. д.).

Тег позволяет узнать, что хранится. указатель сообщает вам, где (либо смещение, либо абсолютное значение в байтах), длина говорит вам, насколько велики данные, а значение – это длина байт данных типа тег. Если вы используете декодер MyFileFormat v1 в файле MyFileFormat v2, указатели позволяют пропускать разделы, которые декодер v1 не понимает. Если вы просто пропускаете недопустимые теги, вы, вероятно, можете просто использовать TLV вместо TPLV.

Я бы либо написал что-то подобное, либо, возможно, определил свой формат в ASN.1. и сгенерировать кодек (я работаю в телекоммуникациях, поэтому ASN.1/TLV мне подходит :-D)

person Community    schedule 27.11.2008

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

Косвенность предпочтительнее в этом случае, поскольку она допускает произвольный доступ, что возможно только в том случае, если все элементы имеют одинаковый размер. Если бы данные хранились непосредственно в массиве без указания местоположения каких-либо записей, доступ к данным в худшем случае занял бы O(n) времени; чтобы ваш код для чтения файлов мог получить доступ к определенному элементу, он должен знать длину всех предыдущих элементов, и единственный способ узнать это — просмотреть каждый из них. Если вы читаете весь файл сразу, вы все равно будете это делать, так что это не будет проблемой. Но если вы хотите только одного, то это не выход.

Принимая во внимание, что с массивом указателей время O (1): все, что вам нужно, это номер индекса, и вы можете получить и следовать указателю, чтобы получить свои данные.

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

person BenW    schedule 19.11.2012
comment
Я нашел ваш ответ интересным и надеялся, что вы могли бы уточнить немного больше. В настоящее время я разрабатываю формат bin и хотел воплотить эту вашу идею. Если у вас есть время, пожалуйста, напишите мне в личку. - person Shane Yost; 25.07.2016