在64位系统上读取32位打包二进制数据

时间:2008-09-25 18:59:42

标签: python c 64-bit

我正在尝试编写一个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 个答案:

答案 0 :(得分:3)

明确指定您的数据类型(例如整数)是32位。否则,如果在读取它们时有两个整数,它们将被读作一个64位整数。

当您处理跨平台问题时,需要注意的两个主要事项是:

  1. 位数。如果打包数据是使用32位整数编写的,那么在读取时,所有代码都必须明确指定32位整数。
  2. 字节顺序。如果将代码从英特尔芯片移到PPC或SPARC,则字节顺序错误。您必须导入数据,然后对其进行字节翻转,以使其与当前体系结构匹配。否则12(0x0000000C)将被读作201326592(0x0C000000)。
  3. 希望这有帮助。

答案 1 :(得分:2)

'struct'模块应该能够做到这一点,尽管数据中间的结构对齐始终是个问题。然而,要做到这一点并不是很难:找出(一次)结构中的结构对齐的边界,然后用手工填充(用'x'说明符)到该边界。您可以通过将struct.calcsize()与实际数据进行比较来双击填充。它肯定比为它编写C扩展更容易。

为了继续使用Py_BuildValue(),你有两个选择。您可以在编译时确定time_t的大小(根据基本类型,所以'int'或'a long'或'ssize_t')然后使用正确的格式字符到Py_BuildValue - 'i'表示int, 'l'为长,'n'为ssize_t。或者您可以手动使用PyInt_FromSsize_t(),在这种情况下,编译器会为您执行向上转换,然后使用“O”格式字符将结果传递给Py_BuildValue。

答案 2 :(得分:2)

您需要确保为结构使用体系结构独立成员。例如,int在一个架构上可以是32位而在另一个架构上可以是64位。正如其他人所建议的那样,请使用int32_t样式类型。如果你的struct包含未对齐的成员,你可能还需要处理编译器添加的填充。

跨架构数据的另一个常见问题是字节序。英特尔i386架构是小端的,但是如果你在一台完全不同的机器上阅读(例如Alpha或Sparc),你也必须担心这一点。

Python struct模块使用作为格式字符串一部分传递的前缀来处理这两种情况。

  • @ - 使用原生大小,字节顺序和对齐方式。 i = sizeof(int),l = sizeof(long)
  • = - 使用本机字节序,但标准大小和对齐方式(i = 32位,l = 64位)
  • < - Little-endian标准尺寸/对齐
  •   
        
    • Big-endian标准尺寸/对齐
    •   

一般情况下,如果数据从您的机器中消失,您应该将字节顺序和大小/填充格式确定为特定的 - 即。使用“<”或“>”作为你的格式。如果要在C扩展中处理此问题,可能需要添加一些代码来处理它。

答案 3 :(得分:1)

您阅读二进制数据的代码是什么?确保您将数据复制到适当大小的类型,例如int32_t,而不仅仅是int

答案 4 :(得分:0)

为什么不使用struct包?