在Python中解析C结构

2024-06-25 12:39:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我肯定这是大错特错了,我有几个问题。我已经将一个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)

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

^{pr2}$

这看起来相对合理。$是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  |................|

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


Tags: 数据代码indata数组findctypes结构
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个字节的块,然后按上面所述进行解码。在

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

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

作为对评论的回应。。。避免寻找捷径。您确实需要深入了解写入文件的位以及它们是如何组织的。为此,您可能需要执行一些hexdump并检查实际的数据表示。根据MShttp://msdn.microsoft.com/en-ca/library/windows/desktop/aa365740(v=vs.85).aspx,这不是一个真正的复杂结构。如果wintypes中的结构不适合您,则可能是您发现了一个bug。磁盘上的结构也可能与内存中的结构不同。通常,ram中的数据结构包括填充以保持16或64字节边界上的对齐。但众所周知,程序员不会将结构按原样转储,而是将其分离并输出到一个减去填充的文件中。因为ctypes/wintypes是用来对DLL进行二进制api调用的,所以它的偏向是在数据布局中包含填充。但文件可能不包括这个。在

相关问题 更多 >