在Python中解析C结构

时间:2013-03-24 07:22:56

标签: python ctypes

我确信这是非常错误的,而且我遇到了一些问题。我已经向磁盘写了一个WIN32_FIND_DATAW结构数组,一个接一个,我想在Python脚本中使用和解析它们。

我目前使用的代码是:

>>> fp = open('findData', 'r').read()
>>> data = ctypes.cast(fp, ctypes.POINTER(wintypes.WIN32_FIND_DATAW))
>>> print str(data[0].cFileName)

第一个问题是第三行没有像我期望的那样打印好的字符串。它打印$Recycle.Bin

而不是打印UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

这是打印存储在那里的数据的结果:

>>> data[0].cFileName
u'\U00520024\U00630065\U00630079\U0065006c\U0042002e\U006e0069'

这看起来相对合理。 $是ASCII 0x24,R是ASCII 0x52,依此类推。

那么为什么我不能像字符串一样打印它?

我的第二个问题是:

>>> data[1].cFileName

给我荒谬的数据。我很确定我没有正确使用ctypes.cast。我应该如何访问这些?为了澄清,在C中,我只是指向缓冲区开头的PWIN32_FIND_DATAW指针,并使用类似的代码访问数组中的各个结构,我正在尝试在Python中执行相同的操作。

更新

这样做的:

>>> data[0].cFileName.encode('windows-1252')

产生此错误:

UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-5: character maps to <undefined>

更新

第一个条目的开头(data[0]直到cFileName的第一部分)如下所示:

user@ubuntu:~/data$ hexdump -C findData | head -n 6
00000000  16 00 00 00 dc 5a 9f d2  31 04 ca 01 ba 81 89 1a  |.....Z..1.......|
00000010  81 e2 cd 01 ba 81 89 1a  81 e2 cd 01 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 24 00 52 00  |............$.R.|
00000030  65 00 63 00 79 00 63 00  6c 00 65 00 2e 00 42 00  |e.c.y.c.l.e...B.|
00000040  69 00 6e 00 00 00 00 00  00 00 00 00 00 00 00 00  |i.n.............|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

如果需要,我可以发布更多数据。

2 个答案:

答案 0 :(得分:2)

正如评论中已经提到的,这是由于Windows和Linux之间的差异。 ctypes模块尝试适应本地环境,因此不匹配。最好的解决方案是使用struct模块以独立于平台的方式处理它。以下代码显示了如何为单个记录执行此操作。

# Setup test data based on incomplete sample
bytes = "\x16\x00\x00\x00\xdc\x5a\x9f\xd2\x31\x04\xca\x01\xba\x81\x89\x1a\x81\xe2\xcd\x01\xba\x81\x89\x1a\x81\xe2\xcd\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x52\x00\x65\x00\x63\x00\x79\x00\x63\x00\x6c\x00\x65\x00\x2e\x00\x42\x00\x69\x00\x6e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
bytes = bytes + "\x00"*(592-len(bytes))

import struct
import codecs

# typedef struct _WIN32_FIND_DATA {
#   DWORD    dwFileAttributes;
#   FILETIME ftCreationTime;
#   FILETIME ftLastAccessTime;
#   FILETIME ftLastWriteTime;
#   DWORD    nFileSizeHigh;
#   DWORD    nFileSizeLow;
#   DWORD    dwReserved0;
#   DWORD    dwReserved1;
#   TCHAR    cFileName[MAX_PATH];
#   TCHAR    cAlternateFileName[14];


fmt = "<L3Q4L520s28s"

attrs, creation, access, write, sizeHigh, sizeLow, reserved0, reserved1, name, alternateName = struct.unpack(fmt, bytes)
name = codecs.utf_16_le_decode(name)[0].strip('\x00')
alternateName = codecs.utf_16_le_decode(alternateName)[0].strip('\x00')
print name

注意:这假设MAX_PATH的大小是260(应该是真的,但你永远不知道)。

要读取文件中的所有值,您需要一次读取592个字节的块,然后按上述方法对其进行解码。

答案 1 :(得分:0)

您应该使用标准库http://docs.python.org/2/library/struct.html中的struct模块,因为您正在解析二进制文件格式。 ctypes模块用于将共享库(DLL)与二进制API集成到Python应用程序中。我不是说你想要做的事情是不可能的,但使用ctypes更简单,只需从二进制文件中解析C结构。

请记住,在C中没有PWIN32_FIND_DATAW指针这样的东西。这只是一个typedef,它将解析为原始C数据类型之一,如32位指针,64位指针等。文件中的数据代表原始基本C数据类型。

回答评论......避免寻找捷径。您确实需要深入了解正在写入文件的位以及它们的组织方式。为此,您可能需要执行一些hexdumps并检查实际的数据表示。根据MS http://msdn.microsoft.com/en-ca/library/windows/desktop/aa365740(v=vs.85).aspx,这不是一个真正复杂的结构。如果wintypes中的结构不适合您,则可能发现了错误。盘上结构也可能与内部结构不同。内部数据结构通常包括填充以保持16或64字节边界上的对齐。但是程序员已经知道不按原样转储结构,而是将它拆开并输出到减去填充的文件。由于ctypes / wintypes用于对DLL进行二进制api调用,因此它的偏差是在数据布局中包含填充。但该文件可能不包含此内容。