让Python与C/C++协同工作 (1)
用Python生成C/C++的数据
Python语言简洁方便易于开发,用来快速的生成和处理数据最好不过了,所以用Python为C/C++生成测试数据在好不过了。但是由于两者在内部的数据存储方面有很大的不同,比如Python的内建类型的float就比C/C++的double要精确的多,所以如果要为C/C++生成二进制的数据(比如通过socket直接发送包含double数据的帧),应该怎么做呢?
其实Python的内建模块中,就至少有这两个是与C/C++联系十分紧密的:
- ctype[A foreign function library for Python]
- struct[Interpret strings as packed binary data]如果仅仅是生成数据,而不是想调用C/C++的那一票函数的话(话说有Python在手,谁还想用那些难用的C/C++函数),用struct模块要更加方便一些。
下面提供一个例子,比如想要生成一个RFC 959[File Transfer Protocol]中的某帧格式如下所示的帧
Byte string: 1 7 8 8 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |0| n | | d(1) | ... | d(n) | +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ^ ^ |---n bytes---| of data如何入手呢?很简单01 # 用于C语言数据封装02 from struct import *03 ...04 # 将要传输的数据,考虑到上面的帧格式,长度定义得比较短...
05 data = "Hello World!"06 # 为将要传输的数据准备一个buffer
07 buff = ""08 # 开始封装
09 buff += pack('!c', chr(len(data)))10 buff += pack('!%ds'%len(data), data)11 # 封装完毕,直接发出去就可以了
在这个代码片段中,核心就是pack函数。而pack函数的核心是格式化字符串,这个东西和printf是差不多的。具体参数说明(引自[Python官方文档](http://docs.python.org/library/struct.html)):Notes:
Format C Type Python Notes x pad byte no value c char string of length 1 b signed char integer B unsigned char integer ? _Bool bool (1) h short integer H unsigned short integer i int integer I unsigned int integer or long l long integer L unsigned long long q long long long (2) Q unsigned long long long (2) f float float d double float s char[] string p char[] string P void * long 1.The ‘?’ conversion code corresponds to the _Bool type defined by C99. If this type is not available, it is simulated using a char. In standard mode, it is always represented by one byte.
New in version 2.6.
2.The ‘q’ and ‘Q’ conversion codes are available in native mode only if the platform C compiler supports C long long, or, on Windows, __int64. They are always available in standard modes.
New in version 2.2.
貌似字符串部分是可以用p取代繁琐的ns的(n表示的是字节数量,在上文中是len(data))。
然后一个需要关心的就是网络字节序的问题,这个也就是上文中pack函数中’!’字段的作用了(引自Python官方文档):
If the first character is not one of these, '@' is assumed.
Character Byte order Size and alignment @ native native = native standard < little-endian standard > big-endian standard ! network (= big-endian) standard 当然如果仅仅是本机调试的话,要注意的问题会少很多,比如字节序的问题就可以忽略。下面再提供一个double型pack的例子:
01 # 用于C语言数据封装02 from struct import *03 ...04 # 对一个时序列来一次fft
05 s = [1, 2, 3, 4, 5, 6, 7, 8]06 so = fft(s)07 # 为将要传输的数据准备一个buffer
08 buff = ""09 # 开始封装
10 for d in so:11 buff += pack('@dd', d.real, d.imag)12 # 封装完毕,直接发出去就可以了
很简单!