Python возвращает неправильную длину строки при использовании специальных символов

У меня есть строка "ault", что я хочу получить длину манипуляции на основе позиций персонажа и так далее. Проблема в том, что первый ë подсчитывается дважды, или я думаю, что ë находится в положении 0 и 'находится в позиции 1.

Есть ли какой-либо возможный способ в Python иметь такой символ, как: - быть представленным как 1?

Я использую кодировку UTF-8 для фактического кода и веб-страницы, на которую она выводится.

edit: Только некоторые сведения о том, зачем мне это нужно. Я работаю над проектом, который переводит английский язык в Сенеку (форма индейского языка), и ë проявляется совсем немного. Некоторые правила перезаписи для определенных слов требуют знания положения букв (самого себя и окружающих букв) и других характеристик, таких как акценты и другие диакритические маркировки.

Ответ 1

UTF-8 - это кодировка юникода, которая использует более одного байта для специальных символов. Если вы не хотите длину закодированной строки, просто декодируйте ее и используйте len() в объекте unicode (а не в объекте str!).

Вот несколько примеров:

>>> # creates a str literal (with utf-8 encoding, if this was
>>> # specified on the beginning of the file):
>>> len('ë́aúlt') 
9
>>> # creates a unicode literal (you should generally use this
>>> # version if you are dealing with special characters):
>>> len(u'ë́aúlt') 
6
>>> # the same str literal (written in an encoded notation):
>>> len('\xc3\xab\xcc\x81a\xc3\xbalt') 
9
>>> # you can convert any str to an unicode object by decoding() it:
>>> len('\xc3\xab\xcc\x81a\xc3\xbalt'.decode('utf-8')) 
6

Конечно, вы также можете получить доступ к одиночным символам в объекте unicode, как если бы вы делали в объекте str (они оба наследуются от basestring и поэтому имеют одинаковые методы):

>>> test = u'ë́aúlt'
>>> print test[0]
ë

Если вы разрабатываете локализованные приложения, обычно рекомендуется использовать только unicode -объекты внутри, путем декодирования всех вводимых вами данных. После завершения работы вы можете снова закодировать результат как "UTF-8". Если вы придерживаетесь этого принципа, вы никогда не увидите, как ваш сервер сбой из-за какого-либо внутреннего UnicodeDecodeError, который вы могли бы получить в противном случае;)

PS: Обратите внимание, что тип данных str и unicode значительно изменился в Python 3. В Python 3 есть только строки unicode и строки простого байта, которые больше нельзя смешивать. Это должно помочь избежать общих ошибок с обработкой unicode...

С уважением, Christoph

Ответ 2

Проблема заключается в том, что первый ë подсчитывается дважды, или я думаю, что ë находится в положении 0 и 'находится в положении 1.

Да. Это то, как кодовые точки определяются Unicode. В общем, вы можете попросить Python преобразовать букву и отдельную "комбинированную диакритическую метку, такую ​​как U + 0301 COMBINING ACUTE ACCENT, используя нормализацию Unicode:

>>> unicodedata.normalize('NFC', u'a\u0301')
u'\xe1' # single character: á

Однако в Unicode нет единого символа для "e с диарезисом и острым акцентом", потому что ни один язык в мире никогда не использовал букву ". (Транслитерация пиньинь имеет" u с диарезисом и острым акцентом ", но не" э.). Следовательно, поддержка шрифтов оставляет желать лучшего; во многих случаях это очень плохо отражается и является беспорядочным блобом в моем веб-браузере.

Чтобы определить, где "редактируемые точки в строке кодов Unicode - это сложная работа, требующая довольно много знаний о языках в языках. Это часть вопроса о" сложном текстовом макете", области, которая также включает в себя такие вопросы, как двунаправленный текст и контекстуальное шейпинг и лигатуры. Для выполнения сложного текстового макета вам понадобится библиотека, такая как Uniscribe в Windows или Pango вообще (для которой существует интерфейс Python).

Если, с другой стороны, вы просто хотите полностью игнорировать все комбинации символов при выполнении счета, вы можете легко избавиться от них:

def withoutcombining(s):
    return ''.join(c for c in s if unicodedata.combining(c)==0)

>>> withoutcombining(u'ë́aúlt')
'\xeba\xfalt' # ëaúlt
>>> len(_)
5

Ответ 3

Лучшее, что вы можете сделать, это использовать unicodedata.normalize(), чтобы разложить символ, а затем отфильтровать акценты.

Не забудьте использовать unicode и символы Unicode в вашем коде.

Ответ 4

Вы сказали: у меня есть строка, которую я хочу, чтобы получить длину манипуляции на основе позиций персонажа и так далее. Проблема в том, что первый ë подсчитывается дважды, или я думаю, что ë находится в положении 0 и 'находится в позиции 1.

Первым шагом в работе над любой проблемой Unicode является то, что вы точно знаете, что находится в ваших данных; не догадываюсь. В этом случае ваша догадка верна; это не всегда будет.

"Именно то, что находится в ваших данных": используйте встроенную функцию repr() (для большего количества вещей, кроме unicode). Полезное преимущество показа результата repr() в вашем вопросе заключается в том, что у ответчиков есть то, что у вас есть. Обратите внимание, что ваш текст отображается только в 4-х позициях вместо 5 с некоторыми браузерами/шрифтами - "e" и его диакритики, а "a" искажены вместе в одной позиции.

Вы можете использовать функцию unicodedata.name(), чтобы сообщить вам, что каждый компонент.

Вот пример:

# coding: utf8
import unicodedata
x = u"ë́aúlt"
print(repr(x))
for c in x:
    try:
        name = unicodedata.name(c)
    except:
        name = "<no name>"
    print "U+%04X" % ord(c), repr(c), name

Результаты:

u'\xeb\u0301a\xfalt'
U+00EB u'\xeb' LATIN SMALL LETTER E WITH DIAERESIS
U+0301 u'\u0301' COMBINING ACUTE ACCENT
U+0061 u'a' LATIN SMALL LETTER A
U+00FA u'\xfa' LATIN SMALL LETTER U WITH ACUTE
U+006C u'l' LATIN SMALL LETTER L
U+0074 u't' LATIN SMALL LETTER T

Теперь прочитайте @bobince ответ: -)

Ответ 5

какую версию Python вы используете? Python 3.1 не имеет этой проблемы.

>>> print(len("ë́aúlt"))
6

Отношения Djoudi