Проблема с.decode('utf-8'). Upper() и специальными символами (но только внутри строки)

Я хотел бы использовать заглавные буквы на заданной позиции в строке. У меня проблема со специальными буквами - польскими, чтобы быть конкретными: например, "ą". Идеально было бы решение, которое работает также для французского, испанского и т. Д. (Ç, è и т. Д.)

dobry="costąm"
print(dobry[4].decode('utf-8').upper())

Я получаю:

  File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)

UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 0: unexpected end of data

пока для этого:

print("ą".decode('utf-8').upper())

Я получаю Ą по желанию.

Что более любопытно для букв на позициях 0-3, то отлично работает, а для:

print(dobry[5].decode('utf-8').upper())

Я получаю ту же проблему

3 ответа

Строка на самом деле выглядит так:

>>> list(dobry)
['c', 'o', 's', 't', '\xc4', '\x85', 'm']

Так, dobry[5] == '\x85' потому что буква represented представлена двумя байтами. Чтобы решить эту проблему, просто используйте Python 3 вместо Python 2.

UTF-8 может использовать более одного байта для кодирования символа, поэтому итерация по байтовой строке и манипулирование отдельными байтами не всегда будут работать. Лучше декодировать в юникодный тип Python 2. Выполните ваши манипуляции, затем перекодируйте в UTF-8.

>>> dobry="costąm"
>>> udobry = unicode(dobry, 'utf-8')
>>> changed = udobry[:4] + udobry[4].upper() + udobry[5]
>>> new_dobry = changed.encode('utf-8')
>>> print new_dobry
costĄm

Как прокомментировал @tripleee, не-ascii символы могут не отображаться в одну кодовую точку Unicode: "ą" может быть одной кодовой точкой U+0105 LATIN SMALL LETTER A с OGONEK или может состоять из "a", за которым следует U+0328 COMBINING ОГОНЕК.

В составленной строке символ "a" может быть заглавным, а "a", за которым следует КОМБИНИРОВАНИЕ OGONEK, приведет к "Ą" (хотя в зависимости от настроек терминала он может выглядеть как два отдельных символа в Python REPL или терминале).).

Обратите внимание, что вам нужно учитывать дополнительный символ при индексации.

Также возможно нормализовать составленную строку к одной версии кода (канонической), используя инструменты в модуле unicodedata:

>>> unicodedata.normalize('NFC', u'costa\u0328m') ==  u"costąm"
True

но это может вызвать проблемы, если, например, вы возвращаете измененную строку в систему, которая ожидает сохранения символа объединения.

Что об этом вместо этого:

print(dobry.decode('utf-8')[5].upper())
Другие вопросы по тегам