python 中 c_char ctypes 的输入类型无效
Invalid input type for c_char ctypes in python
在我的 C 头文件中我有:
long TEST_API test(
_IN____ const char arg1,
_INOUT_ char arg2[512]
);
我在我的 python 代码中导入了 ctypes
并且我正在将“kcOpCode
”和“szXMLAdditionalParameters
”传递给 eftUtility
函数,如下所示:
utilityXml_bytes = bytearray(b'<xml><test>20</test></xml>')
utilityXMLparams = (ctypes.c_byte*512)(*utilityXml_bytes)
def test():
eftUtilityRes = lib.test("s", utilityXMLparams)
if (eftUtilityRes == 10):
"success"
elif(eftUtilityRes == -10):
"type 2 error"
else:
"type 3 error"
但我不断收到“arg1
”的无效输入类型错误。我在这里做错了什么?可能是编码问题?我实际上是在将 s
传递给我的函数吗?
这是因为您没有声明第一个 arg 是一个字节字符串 -- b's'
与 's'
。例如
eftUtilityRes = lib.eftUtility(b"s", utilityXMLparams)
您还需要告诉 ctypes 该函数采用什么参数。有时对于如何转换 python 对象存在歧义。在您的情况下,是一个要解释为 char
或 char*
的单个字符串(两者都是有效的解释)。
lib.eftUtility.argtypes = (ctypes.c_char, ctypes.c_char_p)
此外,您的 Structure
未被使用和不需要,而且您创建可变字节数组的方式很冗长。以下是所有需要的。
xml_data = b'<xml><DisplayWidthInCharacters>20</DisplayWidthInCharacters><JournalKeepDurationInDays>30</JournalKeepDurationInDays></xml>'
utilityXml = ctypes.create_string_buffer(xml_data, 512)
def eftUtilityPepper():
eftUtilityRes = lib.eftUtility(b"s", utilityXml)
if (eftUtilityRes == 10):
"success"
elif(eftUtilityRes == -10):
"type 2 error"
else:
"type 3 error"
您的函数的签名是 (const char arg1 ... )
,因此您必须传递一个字符作为第一个参数,而不是 字符。这意味着一个字节而不是单个 char 字符串。为了测试它,我写了我的 lib
foo.c
#include <stdio.h>
void foo(const char v)
{
printf("Hello, I'm a shared library %c\n",v);
}
并通过
编译
gcc -c -Wall -Werror -fpic foo.c
gcc -shared -o libfoo.so foo.o
现在通过 python 控制台调用它:
我的 python 脚本是:
>>> from ctypes import *
>>> lib = cdll.LoadLibrary("./libfoo.so")
>>> a=lib.foo(ord("s"))
Hello, I'm a shared library s
>>> a=lib.foo("s")
Hello, I'm a shared library D
>>> a=lib.foo(b"s")
Hello, I'm a shared library D
很明显你需要的是ord("s")
.
还有一件事:这只是一个猜测,但您读取的错误可能来自将不正确的字符作为第一个参数读取的库。
通过大量编辑,我们可以看到如果我们不使用 ctypes
调用 C 库函数来使用正确的转换,我们可以犯多少错误的假设或愚蠢的错误。现在我上面写的只是了解问题出在哪里很有用,但应该在 Python 2 和 Python 3 中工作的实际生产代码应该声明正确的函数接口并使用 b"s"
作为参数:
>>> lib.foo.argtypes = (c_char,)
>>> lib.foo.restype = None
>>> lib.foo(b"s")
Hello, I'm a shared library s
仅作记录:
[编辑]
正如@eryksun 在评论中正确指出的那样,不使用正确的强制转换就调用 C 函数是非常不安全的:就像在 C 中不使用原型一样,一切都变成了 int
。所以如果你不设置函数 argtype
最好使用显式转换,如:
>>> a=lib.foo(c_char(ord("s"))) #Work Just on Python 3
Hello, I'm a shared library s
>>> a=lib.foo(c_char("s")) # Just Python 2.7
Hello, I'm a shared library s
>>> a=lib.foo(c_char(b"s")) # Python 2 and 3
Hello, I'm a shared library s
[编辑]
根据您的评论:您还可以使用 105
或十六进制值 0x73
对 ord("s")
进行硬编码
>>> a=lib.foo(105)
Hello, I'm a shared library s
>>> a=lib.foo(0x73)
Hello, I'm a shared library s
[编辑]
正如@Dunes 指出的那样,有一种方法可以告诉 python 如何通过 lib.foo.argtypes = (c_char,)
隐式地进行该转换。但是你如何通过 b"s"
或简单地 "s"
构建你的 char 并不重要(在 python 2.7)。
>>> lib.foo.argtypes = (c_char,)
>>> a=lib.foo(b"s")
Hello, I'm a shared library s
>>> a=lib.foo("s") #Python 2.7
Hello, I'm a shared library s
在Python3中使用b"s"
如果指定argtypes
是必填的:
>>> lib.foo.argtypes = (c_char,)
>>> a=lib.foo(b"s")
Hello, I'm a shared library s
>>> a=lib.foo("s") #Python 3.4
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
在我的 C 头文件中我有:
long TEST_API test(
_IN____ const char arg1,
_INOUT_ char arg2[512]
);
我在我的 python 代码中导入了 ctypes
并且我正在将“kcOpCode
”和“szXMLAdditionalParameters
”传递给 eftUtility
函数,如下所示:
utilityXml_bytes = bytearray(b'<xml><test>20</test></xml>')
utilityXMLparams = (ctypes.c_byte*512)(*utilityXml_bytes)
def test():
eftUtilityRes = lib.test("s", utilityXMLparams)
if (eftUtilityRes == 10):
"success"
elif(eftUtilityRes == -10):
"type 2 error"
else:
"type 3 error"
但我不断收到“arg1
”的无效输入类型错误。我在这里做错了什么?可能是编码问题?我实际上是在将 s
传递给我的函数吗?
这是因为您没有声明第一个 arg 是一个字节字符串 -- b's'
与 's'
。例如
eftUtilityRes = lib.eftUtility(b"s", utilityXMLparams)
您还需要告诉 ctypes 该函数采用什么参数。有时对于如何转换 python 对象存在歧义。在您的情况下,是一个要解释为 char
或 char*
的单个字符串(两者都是有效的解释)。
lib.eftUtility.argtypes = (ctypes.c_char, ctypes.c_char_p)
此外,您的 Structure
未被使用和不需要,而且您创建可变字节数组的方式很冗长。以下是所有需要的。
xml_data = b'<xml><DisplayWidthInCharacters>20</DisplayWidthInCharacters><JournalKeepDurationInDays>30</JournalKeepDurationInDays></xml>'
utilityXml = ctypes.create_string_buffer(xml_data, 512)
def eftUtilityPepper():
eftUtilityRes = lib.eftUtility(b"s", utilityXml)
if (eftUtilityRes == 10):
"success"
elif(eftUtilityRes == -10):
"type 2 error"
else:
"type 3 error"
您的函数的签名是 (const char arg1 ... )
,因此您必须传递一个字符作为第一个参数,而不是 字符。这意味着一个字节而不是单个 char 字符串。为了测试它,我写了我的 lib
foo.c
#include <stdio.h>
void foo(const char v)
{
printf("Hello, I'm a shared library %c\n",v);
}
并通过
编译gcc -c -Wall -Werror -fpic foo.c
gcc -shared -o libfoo.so foo.o
现在通过 python 控制台调用它:
我的 python 脚本是:
>>> from ctypes import *
>>> lib = cdll.LoadLibrary("./libfoo.so")
>>> a=lib.foo(ord("s"))
Hello, I'm a shared library s
>>> a=lib.foo("s")
Hello, I'm a shared library D
>>> a=lib.foo(b"s")
Hello, I'm a shared library D
很明显你需要的是ord("s")
.
还有一件事:这只是一个猜测,但您读取的错误可能来自将不正确的字符作为第一个参数读取的库。
通过大量编辑,我们可以看到如果我们不使用 ctypes
调用 C 库函数来使用正确的转换,我们可以犯多少错误的假设或愚蠢的错误。现在我上面写的只是了解问题出在哪里很有用,但应该在 Python 2 和 Python 3 中工作的实际生产代码应该声明正确的函数接口并使用 b"s"
作为参数:
>>> lib.foo.argtypes = (c_char,)
>>> lib.foo.restype = None
>>> lib.foo(b"s")
Hello, I'm a shared library s
仅作记录:
[编辑]
正如@eryksun 在评论中正确指出的那样,不使用正确的强制转换就调用 C 函数是非常不安全的:就像在 C 中不使用原型一样,一切都变成了 int
。所以如果你不设置函数 argtype
最好使用显式转换,如:
>>> a=lib.foo(c_char(ord("s"))) #Work Just on Python 3
Hello, I'm a shared library s
>>> a=lib.foo(c_char("s")) # Just Python 2.7
Hello, I'm a shared library s
>>> a=lib.foo(c_char(b"s")) # Python 2 and 3
Hello, I'm a shared library s
[编辑]
根据您的评论:您还可以使用 105
或十六进制值 0x73
ord("s")
进行硬编码
>>> a=lib.foo(105)
Hello, I'm a shared library s
>>> a=lib.foo(0x73)
Hello, I'm a shared library s
[编辑]
正如@Dunes 指出的那样,有一种方法可以告诉 python 如何通过 lib.foo.argtypes = (c_char,)
隐式地进行该转换。但是你如何通过 b"s"
或简单地 "s"
构建你的 char 并不重要(在 python 2.7)。
>>> lib.foo.argtypes = (c_char,)
>>> a=lib.foo(b"s")
Hello, I'm a shared library s
>>> a=lib.foo("s") #Python 2.7
Hello, I'm a shared library s
在Python3中使用b"s"
如果指定argtypes
是必填的:
>>> lib.foo.argtypes = (c_char,)
>>> a=lib.foo(b"s")
Hello, I'm a shared library s
>>> a=lib.foo("s") #Python 3.4
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type