2008-11-27 7 views
34

При разработке формата файла для записи двоичных данных, какие атрибуты вы бы назвали форматом? До сих пор, я придумал следующие важные моменты:Какие важные моменты при разработке (двоичного) формата файлов?

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

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

ответ

22

Взгляните на PNG spec. В этом формате есть очень хорошее обоснование.

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

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

+0

PNG - один из примеров. Другие с аналогичной структурой - это IFF (формат файлов обмена, в основном используемый на Commodore Amiga) и RIFF (например, WAV или AVI). – BlaM 2008-11-27 13:42:57

3

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

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

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

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

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

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

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

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

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

2

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

11

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

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

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

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

+0

TLV, кажется, повсюду, так много всего, что я не вижу его везде. – Cheery 2008-12-29 17:37:30

2

Я согласен с предложением 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)

3

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

Сохранение бит в теге предоставляет возможность, возможно, деблокировать блок (если вы оставите данные блока без изменений).

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

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

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

15

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

  1. Магические цифры в начале. Довольно много required in * nix:

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

  3. Спецификация Endianness.

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

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

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

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

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

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

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

А вот некоторые факультативные:

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

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

+0

Почему весь диапазон 0-255 неприемлем для магических чисел, как вы заявили? И каков соответствующий диапазон? Заранее спасибо. – bazz 2017-06-20 16:57:55

4

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

1

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

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

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

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

+0

Я нашел ваш ответ интересным и надеялся, что вы сможете подробнее разобраться. В настоящее время я разрабатываю формат bin и хочу включить эту идею. Если ваше время позволяет мне приветствовать меня. – 2016-07-25 16:51:28

2

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

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

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

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

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

5

Только для записи я нашел this link, что может быть связано с вопросом выше.

Смежные вопросы