Ответ 1
Я думаю, что это было исправлено в 3.3. См:
http://docs.python.org/py3k/whatsnew/3.3.html
http://www.python.org/dev/peps/pep-0393/ (поиск wstr_length
)
Рассмотрим следующий обмен на IPython:
In [1]: s = u'華袞與緼𦅷同歸'
In [2]: len(s)
Out[2]: 8
Правильный вывод должен был быть 7
, но поскольку пятый из этих семи китайских символов имеет высокую кодовую точку Юникода, он представлен в UTF-8 "суррогатной парой", а не только одним простым кодовым пунктом, и в результате Python считает, что это два символа, а не один.
Даже если я использую unicodedata
, который правильно возвращает суррогатную пару в виде одного кодового пункта (\U00026177
), при передаче в len()
возвращается неправильная длина:
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
Не предпринимая таких решительных шагов, как перекомпиляция Python для UTF-32, есть ли простой способ получить правильную длину в таких ситуациях?
Я нахожусь на IPython 0.13, Python 2.7.2, Mac OS 10.8.2.
Я думаю, что это было исправлено в 3.3. См:
http://docs.python.org/py3k/whatsnew/3.3.html
http://www.python.org/dev/peps/pep-0393/ (поиск wstr_length
)
Я делаю функцию для этого на Python 2:
SURROGATE_PAIR = re.compile(u'[\ud800-\udbff][\udc00-\udfff]', re.UNICODE)
def unicodeLen(s):
return len(SURROGATE_PAIR.sub('.', s))
Заменяя суррогатные пары одним символом, мы фиксируем функцию len
. В обычных строках это должно быть довольно эффективно: поскольку шаблон не будет соответствовать, исходная строка будет возвращена без изменений. Он должен работать и с широкими (32-разрядными) сборками Python, так как кодирование суррогатной пары не будет использоваться.
Вы можете переопределить функцию len в Python (см.: Как работает len?) и добавить в нее инструкцию if, чтобы проверить длинный unicode.