Python扩展,包装ICU C++ API
项目描述
PyICU的README文件
欢迎
欢迎来到PyICU,这是一个包装ICU C++库的Python扩展。
ICU代表“国际组件Unicode”。这些是Unicode联盟的i18n库。它们实现了Unicode标准的大部分内容,许多其伴随的Unicode技术标准,以及大部分Unicode CLDR。
PyICU源代码托管在 https://gitlab.pyicu.org/main/pyicu。
ICU主页是 https://icu.unicode.org/
还可以查看CLDR主页 http://cldr.unicode.org/
安装PyICU
PyICU是一个用C++实现的Python扩展,它包装了C/C++ ICU库。它还已知可以作为PyPy扩展工作。除非已经安装了pkg-config
和ICU库以及头文件,否则从PyPI上的源代码构建PyICU不仅需要pip
调用。许多操作系统分发预构建的二进制包的ICU和PyICU,请参见下文。
-
Mac OS X
-
确保ICU已安装且可以通过
pkg-config
(因为icu-config
自ICU 63.1起已被弃用)找到,可以通过遵循ICU构建说明,或使用Homebrew来实现。# install libicu (keg-only) brew install pkg-config icu4c # let setup.py discover keg-only icu4c via pkg-config export PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/icu4c/lib/pkgconfig"
-
使用与您的Python发行版相同的C++编译器安装PyICU (更多信息请参阅 这里)
# EITHER - when using a gcc-built CPython (e.g. from Homebrew) export CC="$(which gcc)" CXX="$(which g++)" # OR - when using system CPython or another clang-based CPython, ensure system clang is used (for proper libstdc++ https://gitlab.pyicu.org/main/pyicu/issues/5#issuecomment-291631507): unset CC CXX # avoid wheels from previous runs or PyPI pip install --no-binary=:pyicu: pyicu
-
ICU和PyICU的二进制文件也都可以通过Macports获得。关于混合二进制文件的限制可能同样适用。
# see versions available /opt/local/bin/port search pyicu sudo /opt/local/bin/port install ...
-
-
Debian
apt-get update # EITHER - from apt directly https://packages.debian.org/source/stable/pyicu apt-get install python3-icu # OR - from source apt-get install pkg-config libicu-dev pip install --no-binary=:pyicu: pyicu
-
Ubuntu:类似于Debian,您可以通过
apt
获得pyicu 软件包。 -
Alpine Linux:您可以通过
apk
获得pyicu 软件包。 -
NetBSD:您可以通过
pkg_add
获得pyicu 软件包。 -
OpenBSD:您可以通过
pkg_add
获得pyicu 软件包。 -
其他操作系统:请参阅下文。
构建PyICU
有关从源代码构建Python、ICU和PyICU的说明,请参阅下一节。本节仅涉及从源代码构建PyICU,同时所有依赖项(如Python和ICU)已存在。
在构建PyICU之前,必须构建并安装ICU库。有关更多信息,请参阅每个系统的说明。
PyICU可以从源代码使用setuptools
构建,也可以使用build
和pip
构建。
-
请确保
pkg-config
可用(自ICU 63.1起,icu-config
程序已被弃用)pkg-config --cflags --libs icu-i18n
如果此命令返回错误或未返回预期的路径,请确保
setup.py
中的INCLUDES
、LFLAGS
、CFLAGS
和LIBRARIES
字典包含您平台上的正确值。对于ICU版本[60, 74],-std=c++11
必须出现在您的CFLAGS中或成为您C++编译器的默认值。从ICU 75开始,-std=c++17
必须出现在您的CFLAGS中或成为您C++编译器的默认值。 -
使用
setuptools
构建和安装PyICUpython setup.py build sudo python setup.py install
-
使用
build
和pip
构建PyICUpython -m build sudo python -m pip install dist/PyICU-<version>-<platform>.whl
-
使用
setuptools
测试PyICUpython setup.py test
-
使用
pytest
测试PyICUpython -m pytest
从源代码构建PyICU、Python 3和ICU
note_855中的说明包含将所有内容从源代码构建到自包含目录的完整步骤,而不修改任何系统目录。它们在M1 Mac上制作和测试过,但也可以修改和用于任何Unix环境。特别是,它们概述了在没有icu-config或pkg-config的情况下从源代码构建PyICU的方法。
运行PyICU
-
Mac OS X 确保在
DYLD_LIBRARY_PATH
中包含包含ICU库的目录的路径。 -
Linux & Solaris 确保在
LD_LIBRARY_PATH
中包含包含ICU库的目录的路径,或者您已将相应的-rpath
参数添加到LFLAGS
中。 -
Windows 确保在
PATH
中包含包含ICU DLLs的目录的路径。
可用内容
请参阅CHANGES文件,以获取更改和新增内容的最新日志。
API文档
PyICU没有API文档。ICU的API文档位于https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/,以下模式可用于将C++ API转换为相应的Python API。
字符串
ICU字符串类型,UnicodeString,是指向一个可变数组UChar Unicode 16位宽字符的类型,详细信息请参见这里。Python 3的str类型在此处和这里进行了描述。Python 2的unicode类型在此处进行了描述。
由于它们之间的差异,当跨越C++边界时,ICU和Python的字符串对象不会合并为同一类型,而是进行转换。
接受UnicodeString
参数的ICU API已被重载,以接受Python 3的str
或Python 2的unicode
对象作为参数。Python 2的str
对象将自动使用utf-8
编码解码为ICU字符串。
要使用编码为非utf-8
的编码将Python 3的bytes
或Python 2的str
对象转换为ICU的UnicodeString
,请使用构造函数UnicodeString(str, encodingName)
。
ICU的C++ API以多种方式接受和返回UnicodeString
参数:按值、按指针或按引用。当一个ICU C++ API被文档记录为接受一个UnicodeString
引用参数时,可以安全地假设存在几个相应的PyICU Python API,使得它以更简单的方式访问。
例如,文档记录为这里的'UnicodeString &Locale::getDisplayName(UnicodeString &)'
API可以从Python以多种方式调用。
-
ICU方式
>>> from icu import UnicodeString, Locale >>> locale = Locale('pt_BR') >>> string = UnicodeString() >>> name = locale.getDisplayName(string) >>> name <UnicodeString: 'Portuguese (Brazil)'> >>> name is string True <-- string arg was returned, modified in place
-
Python方式
>>> from icu import Locale >>> locale = Locale('pt_BR') >>> name = locale.getDisplayName() >>> name 'Portuguese (Brazil)'
分配了一个
UnicodeString
对象并将其转换为Python的str
对象。
UnicodeString
可以使用Python 3的str()
或Python 2的unicode()
构造函数转换为Python unicode字符串。通常的len()
、比较、`[]`和`[:]`操作符都是可用的,而且切片不是只读的,`+=`也是可用的,因为UnicodeString
是可变的。例如
>>> name = locale.getDisplayName()
'Portuguese (Brazil)'
>>> name = UnicodeString(name)
>>> name
<UnicodeString: 'Portuguese (Brazil)'>
>>> str(name)
'Portuguese (Brazil)'
>>> len(name)
19
>>> str(name)
'Portuguese (Brazil)'
>>> name[3]
't'
>>> name[12:18]
<UnicodeString: 'Brazil'>
>>> name[12:18] = 'the country of Brasil'
>>> name
<UnicodeString: 'Portuguese (the country of Brasil)'>
>>> name += ' oh joy'
>>> name
<UnicodeString: 'Portuguese (the country of Brasil) oh joy'>
错误报告
C++ ICU库不使用C++异常来报告错误。ICU C++ API通过一个UErrorCode
引用参数返回错误。所有这样的API都被Python API包装,省略了该参数,并抛出一个ICUError
Python异常。对于同时接受一个ParseError
和一个UErrorCode
的ICU API也是如此,它们都应该省略。
例如,文档记录为这里的'UnicodeString &DateFormat::format(const Formattable &, UnicodeString &, FieldPosition &, UErrorCode &)'
API可以从Python用以下方式调用
>>> from icu import DateFormat, Formattable
>>> df = DateFormat.createInstance()
>>> df
<SimpleDateFormat: M/d/yy h:mm a>
>>> f = Formattable(940284258.0, Formattable.kIsDate)
>>> df.format(f)
'10/18/99 3:04 PM'
当然,也可以使用文档记录为这里的更简单的'UnicodeString &DateFormat::format(UDate, UnicodeString &)'
。
>>> from icu import DateFormat
>>> df = DateFormat.createInstance()
>>> df
<SimpleDateFormat: M/d/yy h:mm a>
>>> df.format(940284258.0)
'10/18/99 3:04 PM'
日期
ICU使用一个称为UDate
的双精度浮点类型,代表自1970年1月1日UTC以来经过的毫秒数,用于日期。
在Python中,time
模块的time()
函数返回自1970年1月1日UTC以来的秒数。由于这个差异,浮点值在传递给接受UDate
的API时要乘以1000,在作为UDate
返回时要除以1000。
带有或没有时区信息的Python的datetime
对象也可以与接受UDate
参数的API一起使用。datetime
对象在进入C++层时会转换为UDate
。
数组
许多ICU API接受数组参数。需要从Python传递一个包含数组元素类型的元素列表。
StringEnumeration
ICU 的 StringEnumeration
有三个 next
方法:next()
返回 str
对象,unext()
在 Python 3 中返回 str
对象,在 Python 2 中返回 unicode
对象,snext()
返回 UnicodeString
对象。这些方法中的任何一个都可以用作迭代器,可以使用 Python 内置的 iter
函数。
例如,假设 e
是一个 StringEnumeration
实例
e = TimeZone.createEnumeration()
[s for s in e] # a list of 'str' objects
[s for s in iter(e.unext, '')] # a list of 'str' or 'unicode' objects
[s for s in iter(e.snext, '')] # a list of 'UnicodeString' objects
时区
ICU 的 TimeZone
类型可以通过 ICUtzinfo
类型包装,以便与 Python 的 datetime
类型一起使用。例如
from datetime import datetime
tz = ICUtzinfo(TimeZone.createTimeZone('US/Mountain'))
datetime.now(tz)
或者,更简单一点
tz = ICUtzinfo.getInstance('Pacific/Fiji')
datetime.now(tz)
要获取默认时区,请使用
defaultTZ = ICUtzinfo.getDefault()
要获取时区的 ID,请使用 tzid
属性或将时区强制转换为字符串
ICUtzinfo.getInstance('Pacific/Fiji').tzid -> 'Pacific/Fiji'
str(ICUtzinfo.getInstance('Pacific/Fiji')) -> 'Pacific/Fiji'
进一步阅读
单元测试 中有更多实际 PyICU 使用的示例。
还有一些从 ICU C/C++ 端口迁移的 示例。
最后但同样重要的是,这份 速查表 提供了一些有用的示例。