Как отмечает ijw , существует несколько способов создать заголовок файла архива.Если кросс-платформенная переносимость вообще будет проблемой - или если вам нужно будет переключаться между 32-битной и 64-битной сборками программного обеспечения на одной и той же платформе - даже тогда, вы должны убедиться, что размеры и компоновкаполя полностью понятны на всех платформах.
Метаданные для каждого файла
Один из способов сделать это - использовать двоичный заголовок фиксированного формата с типами известного размера и порядком байтов.Это то, что предложил ijw.Однако вам нужно будет обрабатывать длинные имена файлов, поэтому вам нужно будет хранить длину (вероятно, в 2-байтовом целом числе без знака), а затем следовать за ней с фактическим путем.
Альтернатива и, как правило,Теперь предпочтительным методом является использование печатных полей (часто называемых форматом ASCII, хотя это неправильно).Время записывается в виде десятичного числа секунд с момента преобразования эпохи в строку и т. Д. Это то, что используют современные архивы ar
;это то, что делает GNU tar
(более или менее; есть некоторые исторические причуды, которые делают это более запутанным);это то, что делает cpio -c
(обычно это по умолчанию в наши дни).Поля могут быть разделены нулями или пробелами;есть простой способ определить конец заголовка;заголовок содержит информацию об имени файла (не обязательно так, как вы бы хотели или ожидаете, но опять-таки, обычно потому, что формат развивался годами), а затем следуют фактические данные.Каким-то образом вы знаете размер каждого поля и файл, который описывает заголовок, чтобы вы могли надежно читать данные.
Эффективность - красная сельдь.Преобразование в / из текстового формата настолько быстрое по сравнению с первым доступом к диску, что практически не возникает проблем с измеримой производительностью.И гарантированная переносимость, как правило, намного перевешивает (микроскопическую) выгоду производительности от использования двоичного формата данных вместо этого - вдвойне, когда двоичные данные должны быть преобразованы на входе или выходе, чтобы получить их в нейтральном для архитектуры формате.
Центральный индекс по сравнению с распределенным индексом
Другой вопрос, который следует рассмотреть, заключается в том, является ли индекс файлов в архиве централизованным (спереди или в конце) или распределенным (метаданные для каждого файла непосредственно предшествуют даннымдля файла).У каждого формата есть некоторые преимущества - как правило, системы используют распределенную версию, потому что вы можете записать информацию для каждого файла, не зная, сколько файлов нужно обработать в общей сложности (например, потому что вы рекурсивно архивируете содержимое каталога).Наличие центрального индекса заранее означает, что вы можете перечислять файлы без чтения всего архива - распределенные метаданные означают, что вы должны прочитать весь файл.Однако центральный индекс усложняет построение архива.
Обратите внимание, что даже при распределенном индексе вам обычно потребуется заголовок для архива в целом, чтобы вы могли определить, что файл имеет форматвы ожидаете.Как правило, существует некоторая информация маркера (!<arch>\n
для архива ar
, обычно; %PDF-1.2\n
в начале файла PDF и т. Д.), Чтобы заверить вас, что файл содержит то, что вы ожидаете.Там могут быть некоторые общие (на уровне архива) метаданные.Затем у вас будут первые метаданные файла, за которыми следуют данные файла, повторяющиеся до конца архива (который может иметь или не иметь формальный маркер конца - больше метаданных).
[H] я бы хотел реализовать его в «двоичном заголовке фиксированного формата», который вы предложили.У меня проблемы с решением, какие команды / функции необходимы.
Я намеревался предложить вам не использовать двоичный заголовок фиксированного формата;Вы должны использовать текстовый формат заголовка.Если вы можете понять, как сделать двоичный формат, будьте моим гостем (я делал это много раз за эти годы - это не значит, что я думаю, что это хорошая идея).
Итак, здесь есть некоторые указатели на формат «текстовый заголовок».
Для метаданных файла вы можете указать, что вы включаете:
- размер
- режим (права доступа, тип)
- владелец
- группа
- время модификации
- длина имени
- имя
Вы можете разумно решить, что размеры вашего файла ограничены 64-разрядными целыми числами без знака, что означает 20 десятичных цифр.Режим может быть напечатан как 16-разрядное восьмеричное число, требующее 6 восьмеричных цифр.Владелец и группа могут быть напечатаны в виде значений UID и GID (а не имени), и в этом случае вы можете использовать 10 цифр для каждого.В качестве альтернативы, вы можете решить использовать имена, но вам следует разрешить имена до 32 символов каждый.Обратите внимание, что имена обычно более переносимы, чем числа.Ни имя, ни номер не имеют большого значения на принимающем компьютере, если вы не извлекаете данные как root (но зачем вам это нужно?).Время модификации - это 32-разрядное целое число со знаком, представляющее количество секунд с начала эпохи (1970-01-01 00: 00: 00Z).Вы должны учесть ошибку Y2038, увеличив количество секунд, превышающее 32-битное значение;вы можете решить, что 12 старших цифр выведут вас из кризиса Y10K (примерно в 4 раза), и это достаточно хорошо;Вы могли бы решить, чтобы позволить на доли секунды тоже.Вместе это говорит о том, что 26 пробелов для метки времени должны быть излишними.Вы можете решить, что каждое поле будет отделено от следующего пробелом (для ясности подумайте «легкость отладки»!).Вы можете разумно решить, что все имена файлов будут ограничены 4 десятичными цифрами общей длины.
Вам необходимо знать, как форматировать типы переносимо - #include <inttypes.h>
ваш друг.
Вызатем разработайте строку формата для печати (записи) метаданных файла и параллельную строку для сканирования (чтения) метаданных файла.
Печать:
"%20" PRIu64 " %06o %-.32s %-.32s %26" PRIu64 " %-4d %s\n"
Это также печатает имя.Завершает заголовок новой строкой.Общий размер составляет 127 байт плюс длина имени файла.Это, вероятно, чрезмерно, но вы можете настроить числа под себя.
Сканирование:
"%" SCNu64 " %o %.32s %.32s %" SCNu64 "%d"
Это не сканирует имя;вам нужно тщательно создать сканер для имени, не в последнюю очередь потому, что вам нужно прочитать пробелы в имени.Фактически, код для сканирования имени пользователя и имени группы также не предполагает пробелов.Если это неприемлемо (то есть имена могут содержать пробелы), то для обработки входных данных вам необходим более сложный формат сканирования или что-то отличное от sscanf()
.
Я предполагаю, что 64-битовое целое для поля времени, вместо того, чтобы смешивать дробные секунды и т. д., даже несмотря на то, что места достаточно, чтобы выделить доли секунды.Скорее всего, вы сэкономите здесь немного места.