注意:在Python 3中使用list('...')
(对于Python 2是u'...'
)通常不会以一般意义上的字符形式给出Unicode字符串;相反,它很可能会导致一系列16位代码点。这对于所有“窄”CPython版本都是正确的,这占了今天Python安装的绝大部分。
当Unicode在1990年代首次提出时,建议使用16位足以涵盖通用文本编码的所有需求,因为它将128个代码点(7位)和256个代码点(8位)转换为高达65,536个代码点。然而,很快就明显,那只是一厢情愿的想法;今天,在Unicode版本5.2中定义了约100,000个代码点,并有数千个待包含的代码点。为了实现这一点,Unicode必须从16位移动到(概念上的)32位(尽管它没有充分利用32位地址空间)。
为了与基于Unicode仍为16位的假设构建的软件保持兼容性,设计了所谓的代理对,其中从特定指定的块中使用两个16位代码点表示超过65,536的代码点,即超出Unicode所称的“基本多语言平面”或BMP,并且被开玩笑地称为该编码的“星体”平面,因为它们相对难以捉摸并且给从事文本处理和编码领域的人带来了不断的头疼。
窄版CPython在某些情况下相当透明地处理代理对,但在其他情况下仍将无法执行正确操作。字符串拆分是其中较为麻烦的一种情况。在窄版Python构建中,list('abc大def')
(或通过转义写作list('abc\u5927\U00027C3Cdef')
)将导致['a'、'b'、'c'、'大'、'\ud85f'、'\udc3c'、'd'、'e'、'f']
,其中'\ud85f'、'\udc3c'
是代理对。顺便说一下,'\ud85f\udc3c'
是JSON标准期望您编写以表示U-27C3C
的内容。这两个代码点中的任何一个单独使用都是无用的;格式良好的Unicode字符串始终只能具有代理对。
因此,要将字符串拆分为字符,你真正需要做的是:
from re import compile as _Re
_unicode_chr_splitter = _Re( '(?s)((?:[\ud800-\udbff][\udc00-\udfff])|.)' ).split
def split_unicode_chrs( text ):
return [ chr for chr in _unicode_chr_splitter( text ) if chr ]
以下正则表达式可以正确返回['a', 'b', 'c', '大', '', 'd', 'e', 'f']
(注意:您可能可以重写正则表达式,使过滤空字符串变得不必要)。
如果你只想将文本分割成汉字,则在此时你几乎已经完成了所有工作。我不确定OP对“单词”的概念是什么,但对我来说,“这是一个句子”可以被等分为“这 | 是 | 一 | 个 | 句子”以及“这是 | 一个 | 句子”,这取决于你的观点。然而,任何超出字符和字符类(符号 vs 空格 vs 字母等)的概念的内容都远远超出了Unicode和Python内置的范畴;您需要进行一些自然语言处理才能做到这一点。让我备注一下,虽然您的示例'yes the United Nations can!'.split()
成功演示了split方法对大量数据的实用性,但它未能正确地将英文文本解析成单词:它未能识别United Nations
为一个单词,同时却错误地认为can!
是一个单词,而这显然不是。该方法会产生误报和漏报。这取决于您的数据和您想要实现的目标,这可能是您想要的,也可能不是。