python - 如何在 python 包装中使用 unicode 字符串为 c++ 类使用 cython?

标签 python c++ cython

我目前正在从事一个宠物项目。我现在的目标是用 cython 为 python 编写一个 c++ 类的包装器。问题是我必须使用俄语文本 (unicode),但是 cython 包装只需要字节,尽管有 c++ 类方法,它能够正确处理 unicode 字符串。我阅读了 Cython 文档并试图在谷歌中找到它,但一无所获。

我如何更改我的代码,以便我的 python 包装器可以采用 unicode 字符串?

这是我的 github 存储库的链接,其中包含当前的代码文件 https://github.com/rproskuryakov/lemmatizer/tree/trie

“trie.pxd”

from libcpp.string cimport string
from libcpp cimport bool

cdef extern from "Trie.cpp":
    pass

# Declare the class with cdef
cdef extern from "Trie.h": 
    cdef cppclass Trie:
        Trie() except +
        void add_word(string word)  # function that should take unicode
        bool find(string word)  # function that should take unicode

“pytrie.pyx”

from trie cimport Trie  # link to according .pxd file

# Create a Cython extension type which holds a C++ instance
# as an attribute and create a bunch of forwarding methods
# Python extension type.
cdef class PyTrie:
    cdef Trie c_tree # Hold a C++ instance which we're wrapping

    def __cinit__(self):
        self.c_tree = Trie()

    def add_word(self, word): 
        return self.c_tree.add_word(word) 

    def find(self, word): 
        return self.c_tree.find(word)

这是我在 python 中得到的。

>>> tree.add_word(b'hello') # works if i got english into ascii
>>> tree.add_word(b'привет') # doesnt work
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "wrapper/pytrie.pyx", line 13, in pytrie.PyTrie.add_word
  File "stringsource", line 15, in string.from_py.__pyx_convert_string_from_py_std__in_string
TypeError: expected bytes, str found

最佳答案

C++ 字符串在内部是一个 char 数组,因此实际上是在“字节”级别而不是 unicode 级别上运行。因此,Cython 不会自动支持 unicode/str <-> std::string 转换。但是,您有两个相当简单的选择:

  1. 使用 unicode/str.encode 函数获取 unicode 对象的字节表示:

    def add_word(self, word):
        if isinstance(word,str): # Python3 version - use unicode for Python 2
            word = word.encode()
        return self.c_tree.add_word(word) 
    

    您必须注意的主要事情是,C++ 用于解释它的编码与 Python 用于对其进行编码的编码相同(Python 默认使用 utf8)。

  2. 转换为 C++ 类型 std::wstring - 内部是一个 wchar_t 数组。不幸的是,Cython 默认不包装 wstring 或提供自动转换,因此您需要编写自己的包装器。使用 Cython wrapping of std::string作为引用——你可能只需要包装构造函数。我用过 the Python C API用于转换为 wchar_t*

    from libc.stddef cimport wchar_t
    
    cdef extern from "<string>" namespace std:
        cdef cppclass wstring:
            wstring() except +
            wstring(size_t, wchar_t) except +
    
            const wchar_T* data()
    
    cdef extern from "Python.h":
         # again, not wrapped by cython a s adefault
         Py_ssize_t PyUnicode_AsWideChar(object o, wchar_t *w, Py_ssize_t size) except -1
    
    # conversion function
    cdef wstring to_wstring(s):
        # create 0-filled output
        cdef wstring out = wstring(len(s),0)
        PyUnicode_AsWideChar(s, <wchar_t*>out.data(),len(s)) # note cast to remove const 
         # I'm not convinced this is 100% acceptable according the standard but practically it should work
        return out
    

您更喜欢这些选项中的哪一个很大程度上取决于您的 C++ 接受的 unicode 字符串。

关于python - 如何在 python 包装中使用 unicode 字符串为 c++ 类使用 cython?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57401078/

相关文章:

python-daemon 启动同一程序的多个实例并传入实例特定的参数

C++-分词器极慢

python - 加速自定义聚合函数

python - 快速迭代多维 numpy 数组中的向量

c++ - cython c++ 对 std::ios_base::failure 的 undefined reference

python - python包的子包

python - 如何使用字典在 python 中创建 'contact book'?

python - 基于 Pandas 中带分隔符的依赖列解析值

c++ - 字符串转wstring,编码问题

java - C/C++/Java 中的运算符