【问题标题】:Passing Char Array With Unknown Encoding使用未知编码传递字符数组
【发布时间】:2021-03-16 06:55:35
【问题描述】:

假设我在 C++ 中有 char content[500],它包含一些未知编码的字符,我想通过 ctypes 回调将它传递给 python 3 代码。

这是我创建的演示代码:

typedef int (*callback)(char *);

extern "C" {
  void foo(callback cb);
}

void foo(callback cb) {
  char hello_world[] = {'h','e','l','l','o','\0','w','o','r','l','d'};
  cb(hello_world);
}

Python 3 代码:

from ctypes import *

callback = CFUNCTYPE(c_int, c_char*11)

def py_callback(b):
    with open('/tmp/test', 'wb') as f:
        f.write(b)
    return 0

cb = cdll.LoadLibrary("/tmp/callback.so")
cb.foo(callback(py_callback))

我以为我会在 /tmp/test 中看到“hello\0world”,但我看到的是以下内容:

# hexdump -C /tmp/test 
00000000  50 5e 46 27 fc 7f 00 00  00 00 00                 |P^F'.......|
0000000b

如果我将回调声明为callback = CFUNCTYPE(c_int, c_char_p),则它无法处理两者之间的空终止字符。

即使两者之间没有以空字符结尾的字符,我也需要调用f.write(b.decode('utf8')) 以使其工作,但这意味着我需要提前知道编码,而我不需要。因此,我只想将数组中的任何字节写入文件。

建议?

【问题讨论】:

  • 有点离题,但你为什么不使用char hello_world[] = "hello\0world";。你知道\0 是一个字符串终止符,对吧?你为什么要包括<string>
  • @JHBonarius 是的,我知道\0 是一个字符串终止符。我故意在上面的示例中使用它来演示我想将完整的数组写入文件,而不是只将字符串(将只是 hello)写入文件。让我放弃无用的#include,我正在玩代码,忘记删除它。
  • @JHBonarius 我查看了您的链接,我尝试将c_char*11 更改为c_ubyte*11,然后将f.write(b) 更改为f.write(str(bytearray(b))),但它在/tmp/test 和5 中给出了bytearray(b'\xc0c\xf6\xef\xfc\x7f\x00\x00\x00\x00\x00') \0 最后似乎不对。

标签: python c++ python-3.x ctypes


【解决方案1】:

c_char_p 具有特殊处理以预期空终止并转换为 Python str。改用POINTER(c_char) 来抑制自动处理,但如果返回的数据不是以空值结尾的,则需要知道它的大小。使用POINTER(c_char),您可以使用正确大小的字符串切片将返回的ctypes.LP_c_char 转换为Python 字符串:

test.cpp

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef void (*CALLBACK)(char *, size_t);

extern "C"
{

API void foo(CALLBACK cb) {
    char hello_world[] = {'h','e','l','l','o','\0','w','o','r','l','d'};
    if(cb)
        cb(hello_world, sizeof hello_world);
}

}

test.py

from ctypes import *

CALLBACK = CFUNCTYPE(None, POINTER(c_char), c_size_t)

@CALLBACK
def py_callback(data,size):
    with open('out.bin', 'wb') as f:
        f.write(data[:size])

dll = CDLL('./test')
dll.foo.argtypes = CALLBACK,
dll.foo.restype = None

dll.foo(py_callback)

out.bin 十六进制转储:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-07
    • 1970-01-01
    相关资源
    最近更新 更多