在Python中解析C结构

在Python中解析C结构,python,ctypes,Python,Ctypes,我确信这是非常错误的,我有几个问题。我已经将一组WIN32\u FIND\u DATAW结构一个接一个地写到磁盘上,我想在Python脚本中使用并解析它们 我当前使用的代码是: >>> fp = open('findData', 'r').read() >>> data = ctypes.cast(fp, ctypes.POINTER(wintypes.WIN32_FIND_DATAW)) >>> print str(data[0].cFil

我确信这是非常错误的,我有几个问题。我已经将一组
WIN32\u FIND\u 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”编解码器无法对位置0-5的字符进行编码:序号不在范围(128)内。

这是仅打印存储在其中的数据的结果:

>>> data[0].cFileName
u'\U00520024\U00630065\U00630079\U0065006c\U0042002e\U006e0069'
这看起来比较合理
$
是ASCII 0x24,
R
是ASCII 0x52,依此类推

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

我的第二个问题是:

>>> data[1].cFileName
给了我荒谬的数据。我相当肯定我没有正确使用
ctypes.cast
。我应该如何访问这些?为了澄清这一点,在C语言中,我只需将一个
PWIN32\u FIND\u DATAW
指针指向缓冲区的开头,并使用类似的代码访问数组中的各个结构,在Python中我也尝试着这样做

更新

做:

>>> data[0].cFileName.encode('windows-1252')
产生以下错误:

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

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

您应该使用标准库中的
struct
模块,因为您正在解析二进制文件格式。
ctypes
模块用于将带有二进制API的共享库(DLL)集成到Python应用程序中。我并不是说您试图做的是不可能的,但是使用
ctypes
比简单地从二进制文件解析C结构要复杂得多

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


在回答评论时。。。避免寻找捷径。您确实需要深入了解写入文件的位以及它们的组织方式。为此,您可能需要执行一些hextumps并检查实际的数据表示。据微软称,这不是一个真正复杂的结构。如果wintypes中的结构不适用于您,则可能是您发现了一个bug。磁盘上的结构也可能与内存中的结构不同。ram中的数据结构通常包括填充,以保持16或64字节边界上的对齐。但众所周知,程序员不会按原样转储结构,而是将其分离并输出到一个不包含填充的文件中。由于ctypes/wintypes用于对DLL进行二进制api调用,因此其偏差在于在数据布局中包含填充。但是该文件可能不包含此内容。

如评论中所述,这是由于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
#基于不完整样本设置测试数据
字节="\X0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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\x00\x00”
字节=字节+“\x00”*(592个字节)
导入结构
导入编解码器
#typedef结构\u WIN32\u查找\u数据{
#DWORD DWFILE属性;
#FILETIME-ftCreationTime;
#FILETIME-ftLastAccessTime;
#FILETIME ftLastWriteTime;
#德沃德·恩弗莱西希格;
#德沃德·恩弗莱西塞洛;
#DWORD dwReserved0;
#德沃德保留1;
#TCHAR cFileName[MAX_PATH];
#TCHAR cAlternateFileName[14];

fmt=“您试过用二进制(
rb
)读取它吗?”?似乎双字节unicode Windows unicode字符被视为4字节linux unicode字符。字符串中的第一个字符不是0x24,而是0x520024。原始文件来自何处?您可以发布一些您试图读取的数据吗?您甚至如何在linux上导入
ctypes.wintypes
?您是否创建了一个新文件
wintypes
模块,从原始版本复制?A
c\u wchar
在Windows上是2个字节,但在其他平台上是4个字节。请显示您在Linux上用于
WIN32\u FIND\u DATAW
的内容。快速查看了python源代码,确认本机wchar\u t用于ctypes.c\u wchar。正在尝试找到解决方案。抱歉,我没有这么想。在structs
c_char
中,数组可能很烦人,因为它们试图创建Python字符串而不是仅仅返回数组。因此它会在第一个null处停止。您需要使用
c_ubyte
来代替。然后它是
bytarray(数据[0].cFileName)。解码('utf-16le'))
。听起来不错。我只是希望使用
wintypes
中已有的
WIN32\u FIND\u DATA
结构。要使用
struct
模块,我现在的问题是如何使用
struct
创建
WIN32\u FIND\u DATA
结构,以及如何
解压
多个
WIN32\u FIND\u数据
文件中的结构?这太棒了。当我在自己的数据集上运行它时,它会打印
␀刀攀挀礀挀氀攀⸀䈀椀渀尽管如此。您的脚本使用您的数据为我正确打印。它在32位Ubuntu上。看起来使用
utf_16_be_decode
解决了我的问题,尽管我不知道为什么。非常感谢你们两位的帮助!@eryksun再次感谢。以前没有使用过那么多结构。@omghai2u在我的示例数据中发现了一个额外字符。update基于上一个版本编辑了它和我的代码
# 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