2015-08-04 3 views
3

Я работаю с русскими словами, написанными в кириллице. Все работает отлично, за исключением того, сколько (но не всех) кириллических символов закодировано как два символа, когда в str. Например:Юникод (кириллица) Индексирование символов, переписывание в питоне

>>>print ["ё"] 
['\xd1\x91'] 

Это не было бы проблемой, если бы я не хотел, чтобы индексировать строки позиций или определить, где характер и заменить его на другой (скажем "e", без диэрезисом). Очевидно, что 2 «символы» рассматриваются как один, когда предваряются с и, как и в u"ё":

>>>print [u"ё"] 
[u'\u0451'] 

str Но s в настоящее время ходило как переменные, и поэтому не может быть с префиксом и, и unicode() дает UnicodeDecodeError (ascii-кодек не может декодировать ...).

Итак ... как мне обойти это? Если это помогает, я использую python 2.7

+0

Тогда можно с префиксом str.format или используя правильную кодировку Юникод –

ответ

2

Здесь есть две возможные ситуации.

Либо ваш str представляет действительные кодированные данные UTF-8, либо нет.

Если он представляет действительные данные UTF-8, вы можете преобразовать его в объект Unicode, используя mystring.decode('utf-8'). После того, как это будет unicode экземпляр, он будет индексироваться символом, а не байтом, как вы уже заметили.

Если в нем есть недопустимые последовательности байтов ... У вас проблемы. Это потому, что вопрос о том, «какой символ делает этот байт, представляет?» больше не имеет четкого ответа. Вам нужно будет решить, что именно вы имеете в виду, когда говорите «третий символ» в присутствии последовательностей байтов, которые на самом деле не представляют конкретный символ Юникода в UTF-8 вообще ...

Возможно, самый простой способ обойти эту проблему - использовать флаг ignore_errors для decode(). Это полностью отменит неверные последовательности байтов и даст вам только «правильные» части строки.

+0

И тогда, если я хочу, чтобы переместить его обратно к исходному двухбайтовому формату, я использую 'mystring.encode ('ascii')'? – sautedman

+0

@sautedman Это не двухбайтовый формат. UTF-8 - это кодирование с переменной длиной. Но да, вы могли бы назвать «кодировать», если хотите. – Borealid

1

Это на самом деле различные кодировки:

>>>print ["ё"] 
['\xd1\x91'] 
>>>print [u"ё"] 
[u'\u0451'] 

Что вы видите является __repr__ «s для элементов в списках. Не версии __str__ объектов юникода.

Но СПО в настоящее время ходило как переменные, и поэтому не может быть приставкой с и

Вы имеете в виду, что данные строки, и должны быть преобразованы в тип юникода:

>>> for c in ["ё"]: print repr(c) 
... 
'\xd1\x91' 

Вы должны насиловать два байта строки в двухбайтовым ширина Юникода:

>>> for c in ["ё"]: print repr(unicode(c, 'utf-8')) 
... 
u'\u0451' 

И вы увидите, что с этим преобразованием они отлично справляются.

1

Чтобы преобразовать байты в Unicode, вы должны знать соответствующую кодировку и вызвать bytes.decode:

>>> b'\xd1\x91'.decode('utf-8') 
u'\u0451' 

Кодирование зависит от источника данных. Это может быть что угодно, например, если данные поступают с веб-страницы; см. A good way to get the charset/encoding of an HTTP response in Python

Не используйте символы, отличные от ascii, в байтах (это явно запрещено в Python 3). Добавить from __future__ import unicode_literals для обработки всех литералов "abc" как литералы Юникода.

Примечание: один пользователь воспринимаемых символов может охватывать несколько Unicode, например, кодовые:

>>> print(u'\u0435\u0308') 
ё 
+0

Я предполагаю, что в вашем последнем примере вы предоставляете «e» с комбинированной диакритикой? – sautedman

+0

Да, это сочетание диарезиса. 'unicodedata.normalize ('NFC', u '\ u0435 \ u0308') == u '\ u0451' == u'ё'' – jfs