Python:结构和数组与ctypes中的类似功能
Python提供了以下三个模块来处理C类型以及如何处理它们:Python:结构和数组与ctypes中的类似功能,python,arrays,struct,ctypes,binary-data,Python,Arrays,Struct,Ctypes,Binary Data,Python提供了以下三个模块来处理C类型以及如何处理它们: 对于C结构 对于类似于C中的数组 对于C函数,这必然需要处理C的类型系统 虽然ctypes似乎比struct和array更通用、更灵活(其主要任务是“Python的外部函数库”),但当任务是读取二进制数据结构时,这三个模块之间的功能似乎有很大的重叠。例如,如果我想读一个C结构 struct MyStruct { int a; float b; char c[12]; }; 我可以使用struct如下: a
- 对于C结构
- 对于类似于C中的数组
- 对于C函数,这必然需要处理C的类型系统
ctypes
似乎比struct
和array
更通用、更灵活(其主要任务是“Python的外部函数库”),但当任务是读取二进制数据结构时,这三个模块之间的功能似乎有很大的重叠。例如,如果我想读一个C结构
struct MyStruct {
int a;
float b;
char c[12];
};
我可以使用struct
如下:
a, b, c = struct.unpack('if12s', b'\x11\0\0\0\x12\x34\x56\x78hello world\0')
print(a, b, c)
# 17 1.7378244361449504e+34 b'hello world\x00'
另一方面,(虽然有点冗长):
(旁白:我确实想知道在这个版本中尾随的'\0'
到哪里去了…)
在我看来,这似乎违反了《Python禅宗》中的原则:
那么,对于二进制数据处理的几个类似模块,这种情况是如何出现的呢?是历史原因还是现实原因?(例如,我可以想象完全省略
struct
模块,只需添加一个更方便的API来读/写C结构到ctypes
)免责声明:这篇文章是基于我对Python stdlib中“分工”的理解,而不是基于事实上可参考的信息
你的问题源于这样一个事实,“C结构”和“二进制数据”倾向于互换使用,这在实践中是正确的,但在技术意义上是错误的。struct
文档也有误导性:它声称可以在“C结构”上工作,而更好的描述应该是“二进制数据”,其中有一些关于C兼容性的免责声明
基本上,struct
、array
和ctypes
做不同的事情<代码>结构处理将Python值转换为内存中的二进制格式<代码>数组处理高效存储大量值的问题<代码>ctypes处理C语言(*)。功能上的重叠源于这样一个事实:对于C,“内存中的二进制格式”是本机的,“有效地存储值”是将它们打包到一个类似C的数组中
您还将注意到,struct
允许您轻松指定endianness,因为它以多种不同的方式处理二进制数据的打包和解包;而在ctypes
中,更难获得非本机字节顺序,因为它使用的是C本机的字节顺序
如果您的任务是读取二进制数据结构,那么抽象级别将不断增加:
int.from_bytes
等转换部分struct
一次性解包ctypes
在这里甚至不需要说明,因为对于这个任务,使用ctypes
几乎需要通过一种不同的编程语言进行往返。事实上,对于你的例子来说,它同样有效是偶然的;它之所以能工作,是因为C语言天生适合表达多种打包二进制数据的方式。但是,例如,如果您的结构是混合endian,则很难在ctypes
中表达。另一个例子是半精度浮点,它没有C等价物(请参阅)
从这个意义上说,ctypes
使用struct
,这也是非常合理的——毕竟,“打包和解包二进制数据”是“与C接口”的一个子任务
另一方面,struct
使用ctypes
:这就像使用电子邮件
库进行字符编码转换一样,因为这是电子邮件库可以完成的任务
(*)嗯,基本上。更精确的是类似“基于C的环境”,即现代计算机如何在低水平上工作,这是由于以C作为主要系统语言的共同进化。尽管这种方式一开始可能并不明显,除非你是荷兰人:-)1。
\0
仍然存在,但未打印(选中NULL
终止字符串)。2.它们可能是并行开发的(我记得第一次,ctypes不在Python标准库中)。功能部分重叠。删除一个这样的模块会破坏向后兼容性,因为可能有很多代码依赖于3个模块中的每一个。注意:ctypes使用struct@CristiFati 1。但是,它是在struct
版本中打印的!另外:len(c)==12
,len(s.c)==11
。2.在向Python3的转换过程中,Python做了很多向后不兼容的更改。据我所知,这些“缺点”/冗余正是Python 3.1想要消除的。是的,您是对的,显然在处理字符数组时,当\0
被禁用时,缓冲区解析(对于该字段)就会停止encountered@CristiFati还有:“ctypes使用struct。”你有关于它的参考资料或详细信息吗?这就是我要找的,谢谢!我想让我产生兴趣的是struct
文档中关于“C structs”的术语/重点(第一句话:“此模块执行Python值和C structs[…]之间的转换”),而实际上,正如您所说,它可以处理更多任意的二进制数据。
class MyStruct(ctypes.Structure):
_fields_ = [
('a', ctypes.c_int),
('b', ctypes.c_float),
('c', ctypes.c_char * 12)
]
s = MyStruct.from_buffer_copy(b'\x11\0\0\0\x12\x34\x56\x78hello world\0')
print(s.a, s.b, s.c)
# 17 1.7378244361449504e+34 b'hello world'