Python如何通过SWIG从C++获取二进制数据(char*)?

4 投票
3 回答
6734 浏览
提问于 2025-04-17 06:29

我在用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 个回答

0

请查看文档中的8.3 C 字符串处理部分。

还有来自文档的内容:

char * 数据类型被处理为一个以 NULL 结尾的 ASCII 字符串。SWIG 将这个类型映射为目标脚本语言中的 8 位字符字符串。在把字符字符串传递给 C/C++ 之前,SWIG 会将目标语言中的字符串转换为以 NULL 结尾的字符串。默认情况下,这些字符串不允许包含嵌入的 NULL 字节。因此,char * 数据类型通常不适合传递二进制数据。不过,可以通过定义 SWIG 类型映射来改变这种行为。有关详细信息,请参见类型映射章节。

8

首先,如果你在处理二进制数据的时候,不应该使用 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)
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)

阅读文档以了解如何使用它。

撰写回答