通过ctypes将Unicode字符串传递给printf

时间:2014-12-14 13:55:44

标签: python c python-3.x unicode utf-8

我正在测试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?

2 个答案:

答案 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, αßΓπΣσµτΦ!