Ответ 1
Прежде всего, в Python2 вам нужно использовать строки Unicode (u'<...>'
) для символов Unicode, которые будут отображаться как символы Unicode. И правильное кодирование источника, если вы хотите использовать сами символы, а не представление \UXXXXXXXX
в исходном коде.
Теперь, согласно Python: получение правильной длины строки, когда она содержит суррогатные пары и Python возвращает длину 2 для одного Unicode символьная строка, в Python2 "узкие" сборки (с sys.maxunicode==65535
), 32-разрядные символы Юникода представлены как суррогатные пары, и это непрозрачно для строковых функций. Это было зафиксировано только в 3.3 (PEP0393).
Простейшее разрешение (за исключением перехода на 3.3+) заключается в компиляции "широкой" сборки Python из источника, как указано в третьей ссылке. В ней символы Unicode - все 4 байта ( таким образом, является потенциальным всплеском памяти), но если вам нужно регулярно обрабатывать широкие символы Unicode, это, вероятно, приемлемая цена.
Решение для "узкой" сборки - , чтобы создать собственный набор строковых функций (len
, slice
; возможно, в качестве подкласса unicode
), который будет определять суррогатные пары и обрабатывать их как один символ. Я не мог легко найти существующую (что странно), но писать ее не так сложно:
- согласно UTF-16 # U + 10000 to U + 10FFFF - Википедия,
- 1-й символ (высокий суррогат) находится в диапазоне
0xD800..0xDBFF
- 2-й символ (низкий суррогат) - в диапазоне
0xDC00..0xDFFF
- эти диапазоны зарезервированы и, следовательно, не могут встречаться как обычные символы
- 1-й символ (высокий суррогат) находится в диапазоне
Итак, вот код для обнаружения суррогатной пары:
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