Чтение 32-битных упакованных двоичных данных в 64-битной системе - PullRequest
2 голосов
/ 25 сентября 2008

Я пытаюсь написать расширение Python C, которое читает упакованные двоичные данные (они сохраняются как структуры структур), а затем анализирует их в объектах Python. На 32-битной машине все работает как положено (двоичные файлы всегда пишутся на 32-битной архитектуре), но не на 64-битной. Есть ли «предпочтительный» способ сделать это?


Было бы много кода для публикации, но в качестве примера:

struct
{
    WORD    version;
    BOOL    upgrade;
    time_t  time1;
            time_t  time2;
} apparms;

File *fp;
fp = fopen(filePath, "r+b");
fread(&apparms, sizeof(apparms), 1, fp);
return Py_BuildValue("{s:i,s:l,s:l}",
  "sysVersion",apparms.version,
  "powerFailTime", apparms.time1,
  "normKitExpDate", apparms.time2
 );

Теперь в 32-битной системе это прекрасно работает, но в 64-битном формате мои time_t отличаются (32-битные и 64-битные).


Черт, вы, люди, быстрые.

Патрик, я изначально начал использовать пакет struct, но обнаружил, что это просто способ замедлить работу для моих нужд. Кроме того, я искал повод написать расширение Python.

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

Спасибо.

Ответы [ 5 ]

3 голосов
/ 25 сентября 2008

Явно укажите, что ваши типы данных (например, целые числа) являются 32-битными. В противном случае, если у вас есть два целых числа рядом друг с другом, когда вы читаете их, они будут прочитаны как одно 64-разрядное целое число.

Когда вы имеете дело с кроссплатформенными проблемами, следует обратить внимание на две основные вещи:

  1. разрядность. Если ваши упакованные данные записаны с 32-битными значениями, тогда весь код должен явно указывать 32-разрядные значения при чтении и записи.
  2. Порядок байтов. Если вы переместите свой код с чипов Intel на PPC или SPARC, ваш порядок байтов будет неправильным. Вам нужно будет импортировать ваши данные, а затем перевернуть их, чтобы они соответствовали текущей архитектуре. В противном случае 12 (0x0000000C) будет читаться как 201326592 (0x0C000000).

Надеюсь, это поможет.

2 голосов
/ 25 сентября 2008

Вы должны убедиться, что вы используете независимые от архитектуры члены для своей структуры. Например, int может быть 32 бита в одной архитектуре и 64 бита в другой. Как предлагали другие, вместо этого используйте типы стилей int32_t. Если ваша структура содержит невыровненные члены, возможно, вам также придется иметь дело с дополнением, добавленным компилятором.

Другая распространенная проблема с данными кросс-архитектуры - это порядковый номер. Архитектура Intel i386 имеет младший порядок, но если вы читаете на совершенно другой машине (например, Alpha или Sparc), вам придется также позаботиться об этом.

Модуль Python struct обрабатывает обе эти ситуации, используя префикс, переданный как часть строки формата.

  • @ - Использовать собственный размер, порядковый номер и выравнивание. я = sizeof (int), l = sizeof (long)
  • = - Использовать собственный порядок байтов, но стандартные размеры и выравнивание (i = 32 бита, l = 64 бита)
  • <- Стандартные размеры с прямым порядком байтов / выравнивание </li>
    • Стандартные размеры с прямым порядком байтов / выравнивание

В общем случае, если данные передаются с вашего компьютера, вы должны привязать порядок байтов и формат / размер к чему-то конкретному, т.е. используйте «<» или «>» в ​​качестве формата. Если вы хотите обработать это в своем расширении C, вам может потребоваться добавить некоторый код для обработки.

2 голосов
/ 25 сентября 2008

Модуль 'struct' должен быть в состоянии сделать это, хотя выравнивание структур в середине данных всегда является проблемой. Однако это не очень сложно сделать правильно: выяснить (однажды), по какой границе выравниваются структуры в структурах, а затем дополнить (вручную, со спецификатором 'x') эту границу. Вы можете перепроверить ваш отступ, сравнив struct.calcsize () с вашими фактическими данными. Это, конечно, проще, чем написать для него расширение C.

Чтобы продолжать использовать Py_BuildValue (), у вас есть два варианта. Вы можете определить размер time_t во время компиляции (в терминах фундаментальных типов, например, int или long или ssize_t), а затем использовать правильный символ формата для Py_BuildValue - i для int, 'l' для долгого времени, 'n' для ssize_t. Или вы можете использовать PyInt_FromSsize_t () вручную, и в этом случае компилятор выполняет для вас обновление, а затем использовать символы формата 'O' для передачи результата в Py_BuildValue.

1 голос
/ 25 сентября 2008

Какой у вас код для чтения двоичных данных? Убедитесь, что вы копируете данные в типы правильного размера, например int32_t вместо просто int.

0 голосов
/ 25 сентября 2008

Почему вы не используете пакет struct ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...