我正在测试Python 3.x的内置ctypes模块,然后花一些时间为我现有的C库创建一个包装器。
我知道C中的stdlib函数需要手册中标有char *
的任何东西的ASCII输入。但是,我的库符合UTF-8标准,我在C程序中测试过它。我还测试了编译C11时的以下代码是否有效并按预期工作:
printf("Hello, %s!\n", u8"world");
但是,如果我在Python中尝试相同的操作,则只打印字符串中的第一个字符。
from ctypes import *
libc = CDLL("libc.so.6")
libc.printf(b"Hello, %s!\n", "world") # will print: Hello, w!
关于Unicode的Python 3手册暗示Python 3使用UTF-8作为其字符编码,这应该避免NUL
看到并停止读取的嵌入式printf
字节。如果我将Python测试中的%s
更改为%ls
,则会按预期打印。
Python实际上是否使用UTF-16?
答案 0 :(得分:1)
Python 3(3.3之前)在内部使用UCS-16或UCS-32,per the docs:
字符串在内部存储为代码点序列(准确地说是Py_UNICODE数组)。根据Python的编译方式(通过--without-wide-unicode或--with-wide-unicode,前者是默认值),Py_UNICODE可以是16位或32位数据类型。
Py_UNICODE
此类型表示Python内部用作保存Unicode序数的基础的存储类型。 Python的默认构建对Py_UNICODE使用16位类型,并在内部将Unicode值存储为UCS2。也可以构建一个UCS4版本的Python(最新的Linux发行版附带了UCS4版本的Python)。然后,这些构建对Py_UNICODE使用32位类型,并在内部将Unicode数据存储为UCS4。
答案 1 :(得分:1)
这一行发生了什么:
libc.printf(b"Hello, %s!\n", "world") # will print: Hello, w!
是ctypes
将字节字符串封送为char*
,将Unicode字符串封送为wchar_t*
(UTF-16或UTF-32,具体取决于操作系统)。 Python内部使用的内容并不重要。我在Windows上,因此我将使用cdll.msvcrt
,但请注意%s
期望char*
和%ls
期望wchar_t*
printf
:
from ctypes import *
cdll.msvcrt.printf(b'Hello, %s!\n', b'world') # byte string
cdll.msvcrt.printf(b'Hello, %ls!\n', 'world') # Unicode string (UTF-16 or UTF-32)
cdll.msvcrt.printf(b'Hello, %s!\n', 'world') # incorrect!
输出:
Hello, world!
Hello, world!
Hello, w!
在示例中只需使用%s
的字节字符串:
libc.printf(b"Hello, %s!\n", b"world")
如果你想要UTF-8,你可以做自己的显式编码:
#coding:utf8
from ctypes import *
cdll.msvcrt.printf(b'Hello, %s!\n', 'αßΓπΣσµτΦ'.encode('utf8'))
输出(通过chcp 65001
更改Windows控制台后,UTF-8代码页):
Hello, αßΓπΣσµτΦ!