Бинарные файлы и кроссплатформенная совместимость - PullRequest
6 голосов
/ 21 декабря 2009

Я написал библиотеку C ++, которая сохраняет мои данные (набор пользовательских структур и т. Д.) В двоичный файл. В настоящее время я использую (т.е. create и потребляем ) файлы локально, на моем компьютере с Windows (XP). Для простоты давайте подумаем о библиотеке в двух частях: Writer (создает файлы) и reader или customer (просто считывает данные из файлов) .

Недавно, однако, я хотел бы также потреблять (т.е. читать) файлы данных, которые я создал на моей машине XP, на моей машине Linux. На этом этапе я должен указать, что обе машины являются ПК (поэтому имеют одинаковую endianess и т. Д.).

Я могу собрать ридер (и скомпилировать для Linux [Ubuntu 9.10, если быть точным]), так как я создатель библиотеки. Мой вопрос, прежде чем я пойду по этому пути (о создании читателя и т. Д.):

Предполагается, что я успешно построил ридер для Linux,

Могу ли я просто скопировать файлы, которые были созданы на машине с Windows (XP) на машину с Linux (Ubuntu 9.10), и использовать программу чтения Linux для успешного чтения скопированного файла?

Ответы [ 5 ]

15 голосов
/ 21 декабря 2009

Для совместимости двоичных файлов:

  • порядковый номер должен совпадать (как и для вас)
  • порядок упаковки битовых полей должен быть таким же
  • размеры и подпись типов должны быть одинаковыми
  • компилятор должен принять те же решения относительно отступов и выравнивания

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

2 голосов
/ 10 августа 2016

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

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

Это работает, но уродливо.Не говоря уже о том, что легко ошибиться.

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

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

Есть один недостаток;индивидуальное чтение / запись полей будет медленнее, чем просто использование fread / fwrite напрямую.Вы можете настроить буфер (uint8_t buffer[]) и записать в него все данные, а затем записать все сразу, что может помочь, но все равно будет медленнее (потому что вам все равно придется перемещатьполя в буфер по одному), но для большинства целей это все равно будет достаточно быстро (за исключением встроенных систем / систем реального времени или чрезвычайно высокопроизводительных вычислений).

2 голосов
/ 21 декабря 2009

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

Проблема, с которой вы можете столкнуться в своем коде, заключается в размере целых чисел, вы не обязательно должны предполагать, что компилятор в разных ОС имеет одинаковый размер int. Так что либо скопируйте блоки байтов и приведите их, либо используйте int16, int32 и т. Д.

1 голос
/ 21 декабря 2009

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

1 голос
/ 21 декабря 2009

Если:

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

тогда да, ваши файлы должны быть переносимыми.

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

...