Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

Длина строки считается неверно: unicode в python 2.7

#python #строки #python_2x #unicode


Вычисляю длину строки. Но длина строки (наверное), считается удвоенной для русских
символов. Для цифр -- нет. Например:

len("ул Весёлая") == 1 + 2 * 9 // (Пробел + русские символы (*2))
len("ул Весёлая 71") == 1 + 2 * 9 + 1 + 3 // (Пробел + русские символы (*2) + пробел
+ 2 цифры)


Есть ли функция в питоне, которая считает истиную длину строки, а не количество байтов
(считается, наверное, именно кол-во байтов).
    


Ответы

Ответ 1



Проблема не возникает, если использовать правильные(unicode) строки: >>> print(len('ул Весенняя 13')) 24 >>> print(len(u'ул Весенняя 13')) 14 Можно самостоятельно декодировать простые строки: >>> print(len('ул Весенняя 13'.decode("utf-8"))) 14 В python3 нет такой проблемы, потому что строки по умолчанию в utf-8: >>> print(len('ул Весенняя 13')) 14 >>> print(len(u'ул Весенняя 13')) 14 В общем случае(полный набор символов unicode) задача найти длину строки не является тривиальной. Во первых, нужно определиться с самим термином - что понимать под длиной строки? Варианты: количество байт в utf-8 представлении (24 выше) количество unicode символов (14 выше) можно посчитать, как количество всех байт, кроме диапазона (0x80-0xBF). количество видимых знаков количество занятых знакомест Нужно исключить диакритику и прочие символы, не занимающие места Положение усугубляется ещё и тем, что одни и те же строки могут быть закодированы разными последовательностями unicode codepoints. Для определённости можно "нормализовать" строку, например, с помощью unicodedata.normalize('NFC', ustr) >>> len(regex.findall(ur'[\0-\u02FF\u0370-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uDC00-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF][\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]', unicodedata.normalize('NFC', u'Z͑ͫ̓ͪ̂ͫ̽͏̴̙A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘!̿̋ͥͥ̂ͣ̐́́͞'))) 6

Ответ 2



Константы (string literals) "" создают байтовые строки в Питоне 2 (если from __future__ import unicode_literals не включено). Поэтому len("abc") возвращает количество байт. u"" является строковой константой, которая всегда Unicode строки создаёт. len(u"abc") вернёт количество Юникодных символов (Unicode codepoint). Некоторые буквы (user-perceived characters) могут состоять из нескольких символов. В этом случае, можно \X регулярное выражение использовать, чтобы найти количество "неразрывных/слитных" элементов текста (eXtended grapheme clusters): >>> import regex # $ pip install regex >>> text = u'я 😂 ё' >>> print(repr(text)) u'\u044f \U0001f602 \u0435\u0308' # 6 code points >>> regex.findall(ur'\X', text) # 5 grapheme clusters [u'\u044f', u' ', u'\U0001f602', u' ', u'\u0435\u0308'] >>> print " | ".join(regex.findall(ur'\X', text)) я | | 😂 | | ё # 5 user-perceived characters Дополнительно, существуют так называемые узкие сборки Питона 2, в которых не BMP-символы такие как 😂 представляются в виде utf-16 суррогатной пары (surrogate pair) — нарушение абстракции, что Юникодная строка является неизменяемой последовательностью Unicode сodepoint. Вот ещё пример одного знака (emoji), который из нескольких юникод-cимволов состоит: >>> emoji = u'\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466' >>> print(emoji) 👩‍👩‍👧‍👦 >>> len(emoji) 7 >>> len(regex.findall(u'\X', emoji)) 1 С точки зрения движения курсора, выделения, копирования, итд — этот эмодзи это один GUI элемент. Как разбить строку на отдельные символы? Разделить в Python 3 слово на символы Для текстовых интерфейсов может иметь значение ширина напечатанной строки в терминале. python-prompt-toolkit использует wcwidth модуль: >>> import wcwidth # $ pip install wcwidth >>> wcwidth.wcswidth(emoji) 8

Комментариев нет:

Отправить комментарий