Python如何通过SWIG从C++获取二进制数据(char*)?
我在用SWIG把C++的函数放到Python里时,遇到了一个问题。
当我从C++传一个字符指针(char *)到Python时,Python把这个字符指针截断了。
举个例子:
example.h:
char * fun()
{
return "abc\0de";
}
现在在Python里,我们调用 example.fun() 结果只打印出 "abc" 而不是 "abc\0de" 因为'\0'后面的数据在Python里被删掉了。
我想从C++的fun()函数中获取所有的字符(这是一种可以包含'\0'的二进制数据),如果有任何建议,我都很感激。
3 个回答
请查看文档中的8.3 C 字符串处理部分。
还有来自文档的内容:
char * 数据类型被处理为一个以 NULL 结尾的 ASCII 字符串。SWIG 将这个类型映射为目标脚本语言中的 8 位字符字符串。在把字符字符串传递给 C/C++ 之前,SWIG 会将目标语言中的字符串转换为以 NULL 结尾的字符串。默认情况下,这些字符串不允许包含嵌入的 NULL 字节。因此,char * 数据类型通常不适合传递二进制数据。不过,可以通过定义 SWIG 类型映射来改变这种行为。有关详细信息,请参见类型映射章节。
首先,如果你在处理二进制数据的时候,不应该使用 char *
,因为 swig 会把它当成普通字符串来处理。你应该使用 void *
。swig 提供了一个叫做 'cdata.i' 的模块——你需要在接口定义文件中包含它。
一旦你包含了这个模块,它会提供两个函数:cdata()
和 memmove()
。
- 给定一个
void *
和二进制数据的长度,cdata()
可以把它转换成目标语言的字符串类型。 memmove()
则是反过来的——给定一个字符串类型,它会把字符串的内容(包括里面的空字节)复制到 C 的void*
类型中。
使用这个模块处理二进制数据会简单很多。希望这正是你需要的。
example.i
%module example
%include "cdata.i"
%{
void *fun()
{
return "abc\0de";
}
%}
test.py
import example
print example.cdata(example.fun(), 6)
C/C++中的字符串是以NULL结尾的,也就是说,第一个\0
字符表示字符串的结束。
当一个函数返回指向这样的字符串的指针时,调用者(在这个例子中是SWIG)无法知道在第一个\0
之后是否还有更多的数据,所以你只能得到字符串的第一部分。
因此,首先要做的就是修改你的C函数,让它不仅返回字符串,还返回字符串的长度。因为一个函数只能返回一个值,所以我们将使用指针参数来实现。
void fun(char** s, int *sz)
{
*s = "abc\0de";
*sz = 6;
}
SWIG的文档建议使用cstring.i
库来包装这样的函数。特别是,最后一个宏正好满足你的需求。
%cstring_output_allocate_size(parm, szparm, release)
阅读文档以了解如何使用它。