首先,在Python2中,您需要使用Unicode字符串(
u'<...>'
)才能将Unicode字符视为Unicode字符。如果要使用字符本身而不是源代码中的
\UXXXXXXXX
表示,请使用
正确的源编码。
现在,根据Python: getting correct string length when it contains surrogate pairs和Python returns length of 2 for single Unicode character string,在Python2的“窄”构建中(具有sys.maxunicode==65535
),32位Unicode字符被表示为代理对,并且这对于字符串函数来说并不透明。这只在3.3中得到修复(PEP0393)。
最简单的解决方案(除了迁移到3.3+之外)是按照第三个链接中概述的方式从源代码编译Python“宽”构建。在其中,Unicode字符都是4字节(因此可能占用大量内存),但是如果您需要经常处理宽Unicode字符,则这可能是可以接受的代价。
“窄”构建的解决方案是创建一组自定义字符串函数(len
,slice
;可能作为unicode
的子类),该函数将检测代理对并将其视为单个字符。我无法立即找到现有的解决方案(这很奇怪),但编写起来并不太难:
这里是检测代理对的代码:
def is_surrogate(s,i):
if 0xD800 <= ord(s[i]) <= 0xDBFF:
try:
l = s[i+1]
except IndexError:
return False
if 0xDC00 <= ord(l) <= 0xDFFF:
return True
else:
raise ValueError("Illegal UTF-16 sequence: %r" % s[i:i+2])
else:
return False
以及一个返回简单切片的函数:
def slice(s,start,end):
l=len(s)
i=0
while i<start and i<l:
if is_surrogate(s,i):
start+=1
end+=1
i+=1
i+=1
while i<end and i<l:
if is_surrogate(s,i):
end+=1
i+=1
i+=1
return s[start:end]
在这里,你需要付出性能的代价,因为这些函数比内置函数慢得多:
>>> ux=u"a"*5000+u"\U00100000"*30000+u"b"*50000
>>> timeit.timeit('slice(ux,10000,100000)','from __main__ import slice,ux',number=1000)
46.44128203392029 #msec
>>> timeit.timeit('ux[10000:100000]','from __main__ import slice,ux',number=1000000)
8.814016103744507 #usec
x ='xyz'
是非法的(或者可能会被误解)。 - ivan_pozdeevu'...'
字符串字面量表示值。这也凸显出这个问题缺少一个实际的[mcve]。要么是缺少from __future__ import unicode_literals
,要么是缺少x
字符串定义前缀的u
。 - Martijn Pieters