Python中将补充码点添加到Unicode字符串

3

unichr(0x10000)在未使用--enable-unicode=ucs4编译的cpython中会产生ValueError错误。

是否有一种语言内置或核心库函数能够将任意Unicode标量值或代码点转换为unicode字符串,而无论程序运行在何种类型的python解释器上都能正常工作?


我非常确定这是不可能完成的,这也是你不能信任别人的Python在任意Unicode数据上运行的原因之一。然而,在v3.3版本中似乎已经修复了这个问题。如果你想要抽象的Unicode,你必须等待下一个版本发布,或者使用更强大的平台。 - tchrist
@tchrist,谢谢。是的,我需要学习Python3.x。它似乎解决了许多小的困惑源。 - Mike Samuel
我(大多数时候)不同意@tchrist的观点,认为这是可以做到的;请看下面我的回答,我已经做到了。 - Jim DeLaHunt
1个回答

8

Yes, here you go:

>>> unichr(0xd800)+unichr(0xdc00)
u'\U00010000'

理解的关键点是,unichr()将整数转换为Python解释器字符串编码中的单个代码单元。Python标准库文档2.7.3中,2.内置函数unichr()描述如下:

返回Unicode代码为整数i的一个字符的Unicode字符串。参数的有效范围取决于Python的配置-它可以是UCS2 [0..0xFFFF]或UCS4 [0..0x10FFFF]。否则会引发ValueError

我强调了“一个字符”,这意味着Unicode术语中的一个代码单元
我假设您正在使用Python 2.x。 Python 3.x解释器没有内置的unichr()函数。而是2.内置函数chr()描述如下:

返回表示Unicode代码点是整数i的字符的字符串。参数的有效范围是从0到1,114,111(在基数16中为0x10FFFF)。

请注意,返回值现在是未指定长度的字符串,而不是单个代码单元的字符串。因此,在Python 3.x中,chr(0x10000)将按您预期的方式工作。它“将任意Unicode标量值或代码点转换为适用于运行程序的Python解释器类型无关的unicode字符串”。
但回到Python 2.x。如果您使用unichr()创建Python 2.x unicode对象,并且使用的Unicode标量值大于0xFFFF,则您正在使您的代码意识到Python解释器对unicode对象的实现。
您可以编写一个函数来隔离这种意识,该函数尝试对标量值使用unichr(),捕获ValueError,并使用相应的UTF-16代理项再次尝试:
def unichr_supplemental(scalar):
     try:
         return unichr(scalar)
     except ValueError:
         return unichr( 0xd800 + ((scalar-0x10000)//0x400) ) \
               +unichr( 0xdc00 + ((scalar-0x10000)% 0x400) )

>>> unichr_supplemental(0x41),len(unichr_supplemental(0x41))
(u'A', 1)
>>> unichr_supplemental(0x10000), len(unichr_supplemental(0x10000))
(u'\U00010000', 2)

但是你可能会发现,把你的标量转换成UTF-32四字节值放在一个UTF-32字节 string 中,然后将这个字节 string 解码成一个 unicode 字符串更容易:

>>> '\x00\x00\x00\x41'.decode('utf-32be'), \
... len('\x00\x00\x00\x41'.decode('utf-32be'))
(u'A', 1)
>>> '\x00\x01\x00\x00'.decode('utf-32be'), \
... len('\x00\x01\x00\x00'.decode('utf-32be'))
(u'\U00010000', 2)

以上代码在Python 2.6.7上进行了测试,采用UTF-16编码处理Unicode字符串。我没有在Python 2.x解释器中使用UTF-32编码处理Unicode字符串对其进行测试。但是,在任何Python 2.x解释器中使用任何Unicode字符串实现方式都应该能够正常运行。


好的回答。请注意,最近的Python版本已经解决了整个“宽字符编译”问题,这也极大地帮助了这些事情。如果您正在运行早期版本,则应该使用“宽字符编译”。 - tchrist
你关于2.x的说法是正确的。感谢你指出规范的细节并解释它们之间的差异。 - Mike Samuel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接