Как получить надежный счет символа Юникода в Python?

Google App Engine использует Python 2.5.2, видимо, с поддержкой UCS4. Но хранилище данных GAE использует UTF-8 внутренне. Поэтому, если вы храните u '\ ud834 \ udd0c' (длина 2) в хранилище данных, когда вы его извлекаете, вы получаете '\ U0001d10c' (длина 1). Я пытаюсь подсчитать количество символов Юникода в строке таким образом, чтобы дать тот же результат до и после его хранения. Поэтому я пытаюсь нормализовать строку (от u '\ ud834 \ udd0c' до '\ U0001d10c), как только я ее получу, прежде чем вычислять ее длину и помещать ее в хранилище данных. Я знаю, что могу просто кодировать его в UTF-8, а затем снова декодировать, но есть ли более простой / эффективный способ?

2 Solutions collect form web for “Как получить надежный счет символа Юникода в Python?”

Я знаю, что могу просто кодировать его в UTF-8, а затем снова декодировать

Да, это обычная идиома, чтобы решить проблему, когда у вас есть «суррогаты UTF-16 в строке UCS-4». Но, как сказал Механическая улитка, этот вход искажен, и вы должны исправлять все, что было произведено в предпочтении.

есть ли более простой / эффективный способ?

Ну … вы можете сделать это вручную с помощью регулярного выражения, например:

 re.sub( u'([\uD800-\uDBFF])([\uDC00-\uDFFF])', lambda m: unichr((ord(m.group(1))-0xD800<<10)+ord(m.group(2))-0xDC00+0x10000), s ) 

Конечно, не более простой … У меня также есть сомнения относительно того, действительно ли это эффективнее!

К сожалению, поведение интерпретатора CPython в версиях выше 3.3 зависит от того, построена ли она с помощью «узкой» или «широкой» поддержки Unicode. Таким образом, один и тот же код, например вызов len , может иметь другой результат в разных строках стандартного интерпретатора. См. Этот вопрос для примера.

Различие между «узким» и «широким» заключается в том, что «узкие» интерпретаторы внутренне хранят 16-битные кодовые единицы (UCS-2), тогда как «широкие» интерпретаторы внутренне хранят 32-битные кодовые единицы (UCS-4). Точки кода U + 10000 и выше (за пределами базовой многоязычной плоскости) имеют две len в «узких» интерпретаторах, потому что для их представления необходимы два кодовых блока UCS-2 (с использованием суррогатов), и это то, что измеряется. Для «широких» построений для кодовой точки , отличной от BMP, требуется только один блок кода UCS-4, поэтому для этих построений len является одним для таких кодовых точек.

Я подтвердил, что ниже обрабатываются все строки unicode независимо от того, содержат они суррогатные пары или нет, и работают в CPython 2.7 как узкие, так и широкие сборки. (Возможно, указание строки, такой как u'\ud83d\udc4d' в широком интерпретаторе, отражает утвердительное желание представить полную суррогатную кодовую точку, отличную от единицы кода частичного символа, и поэтому автоматически не исправляется ошибка, но Я игнорирую это здесь. Это краевой кейс и, как правило, не желаемый вариант использования.)

Используемый @invoke трюк @invoke – это способ избежать повторных вычислений, не добавляя ничего в __dict__ модуля.

 invoke = lambda f: f() # trick taken from AJAX frameworks @invoke def codepoint_count(): testlength = len(u'\U00010000') # pre-compute once assert (testlength == 1) or (testlength == 2) if testlength == 1: def closure(data): # count function for "wide" interpreter u'returns the number of Unicode code points in a unicode string' return len(data.encode('UTF-16BE').decode('UTF-16BE')) else: def is_surrogate(c): ordc = ord(c) return (ordc >= 55296) and (ordc < 56320) def closure(data): # count function for "narrow" interpreter u'returns the number of Unicode code points in a unicode string' return len(data) - len(filter(is_surrogate, data)) return closure assert codepoint_count(u'hello \U0001f44d') == 7 assert codepoint_count(u'hello \ud83d\udc4d') == 7 
Python - лучший язык программирования в мире.