Проблема с.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())