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