Python:在包含代理项对时获取正确的字符串长度

Python: getting correct string length when it contains surrogate pairs

考虑在IPython上进行以下交换:

1
2
3
4
In [1]: s = u'華袞與緼??同歸'

In [2]: len(s)
Out[2]: 8

正确的输出应该是7,但是由于这七个汉字中的第五个有很高的Unicode码位,所以它用"代理项对"而不是一个简单的码位来表示,因此python认为它是两个字符而不是一个。

即使我使用unicodedata,它将代理项对正确地返回为单个码点(\U00026177),当传递给len()时,仍然返回错误的长度:

1
2
3
4
5
6
7
8
In [3]: import unicodedata

In [4]: unicodedata.normalize('NFC', s)
Out[4]: u'\u83ef\u889e\u8207\u7dfc\U00026177\u540c\u6b78'


In [5]: len(unicodedata.normalize('NFC', s))
Out[5]: 8

如果不采取像为utf-32重新编译python这样的极端步骤,有没有一种简单的方法可以在这种情况下获得正确的长度?

我在i python 0.13,python 2.7.2,mac os 10.8.2上。


我认为这已经在3.3中得到了解决。见:

http://docs.python.org/py3k/whatsnew/3.3.htmlhttp://www.python.org/dev/peps/pep-0393/(搜索wstr_length)


我在python 2上做了一个函数:

1
2
3
SURROGATE_PAIR = re.compile(u'[\ud800-\udbff][\udc00-\udfff]', re.UNICODE)
def unicodeLen(s):
  return len(SURROGATE_PAIR.sub('.', s))

通过将代理对替换为单个字符,我们"修复"了len函数。在普通字符串上,这应该是相当有效的:因为模式不匹配,所以返回原始字符串时不做任何修改。它也应该适用于宽(32位)的Python构建,因为不会使用代理项对编码。


您可以重写python中的len函数(请参见:len是如何工作的?)并在其中添加一个if语句以检查超长的unicode。