create_string_buffer 报错 TypeError: 期望 str/bytes 类型但得到 str 实例

16 投票
3 回答
18814 浏览
提问于 2025-04-17 00:35

我正在尝试这个简单的ctypes示例,但遇到了上面提到的错误。

>>> from ctypes import create_string_buffer
>>> str = create_string_buffer("hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python32\lib\ctypes\__init__.py", line 59, in create_string_buffer
buf.value = init
TypeError: str/bytes expected instead of str instance

有没有人知道我哪里做错了?

顺便问一下,我想从我的Python代码中把一个指向字符串的指针传递给一个C函数,这样我就可以在那边进行一些字符串操作,然后返回另一个字符串。有人能给我一些示例代码,教我怎么做吗?

extern "C" __declspec(dllexport) char * echo(char* c)
{
      // do stuff
    return c;
}

3 个回答

0

这个参数是你需要的字符串缓冲区的大小,所以你要把缓冲区应该有多长传给它。这样它就会创建一个那个大小的字符数组。

>>> from ctypes import create_string_buffer
>>> buf = create_string_buffer(20)
>>> buf
<ctypes.c_char_Array_20 object at 0x355348>
>>> 
15

你正在使用Python 3,在Python 3中,字符串(str类型)是Unicode对象。create_string_buffer是用来创建C语言的char数组的,所以你需要传入一个bytes对象。如果你有一个str对象,可以用合适的编码方式把它转换成bytes对象。值得注意的是,还有一个create_unicode_buffer,它可以创建C语言的wchar数组,并且可以接受Python 3的str对象。

Python 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.create_unicode_buffer('abcd')
<ctypes.c_wchar_Array_5 object at 0x00C2D300>
>>> ctypes.create_string_buffer(b'abcd')
<ctypes.c_char_Array_5 object at 0x00C2D1C0>
>>> ctypes.create_string_buffer('abcd'.encode('utf-8'))
<ctypes.c_char_Array_5 object at 0x00C2D300>

关于你问题的第二部分,你是想修改现有的字符串,还是想返回一个全新的字符串?如果是前者,可以使用create_string_buffer来传递一个可变的字符串给函数,这样在原地修改就可以了。下面是一个简单的例子,调用Windows MSVCRT库中的<_strupr()>:

Python 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> c = ctypes.CDLL('msvcr90.dll')
>>> strupr = c._strupr
>>> a=ctypes.create_string_buffer(b'abc')
>>> strupr.restype=ctypes.c_char_p
>>> strupr(a)
b'ABC'

注意,设置函数的restype(返回类型)为c_char_p,这会告诉ctypes把返回值转换成bytes对象。另外要注意,_strupr是直接在原地修改字符串的,所以必须传递一个可变的字符串缓冲区。只把Python的字节字符串传给那些接受const char*的C函数,因为通过ctypes直接修改Python字符串是不好的TM

11

关于让它正常工作,如果你传入一个 bytes 对象,它就能正常运行:

>>> import ctypes
>>> ctypes.create_string_buffer(b'hello')
<ctypes.c_char_Array_6 object at 0x25258c0>

看看 create_string_buffer 的代码:

def create_string_buffer(init, size=None):
    """create_string_buffer(aBytes) -> character array
    create_string_buffer(anInteger) -> character array
    create_string_buffer(aString, anInteger) -> character array
    """
    if isinstance(init, (str, bytes)):
        if size is None:
            size = len(init)+1
        buftype = c_char * size
        buf = buftype()
        buf.value = init
        return buf
    elif isinstance(init, int):
        buftype = c_char * init
        buf = buftype()
        return buf
    raise TypeError(init)

直接这样做,

>>> (ctypes.c_char * 10)().value = b'123456789'

这样也没问题。

>>> (ctypes.c_char * 10)().value = '123456789'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: str/bytes expected instead of str instance

这显示了相同的行为。看起来你发现了一个bug。

是时候去看看 http://bugs.python.org 了。这里有一些和 c_char 以及 create_string_buffer 相关的bug,它们在同一个领域,但没有报告说现在传入 str 会失败(不过确实有例子显示在Py3K中以前是可以的)。

撰写回答