Юникод форматирование - PullRequest
0 голосов
/ 22 октября 2018

Я работаю с форматированием строки.Для английского языка форматирование аккуратное, но для символов юникода форматирование случайное.Может кто-нибудь сказать, пожалуйста, причину?Пример:

form = u'{:<15}{:<3}({})'
a = [
 u'സി ട്രീമിം',
 u'ബി ഡോഗേറ്റ്',
 u'ജെ ഹോളണ്ട്',
 u'എം നസീർ ',
 u'എം ബസ്ചാഗൻ…',
 u'ടി ഹെഡ് ',
 u'കെ ഭാരത് ',
 u'എം സിറാജ് ',
 u'എ ഈശ്വരൻ ',
 u'സി ഹാൻഡ്‌സ്‌കോംബ് ബി',]

 for i in range(0, 10):
     print form.format(a[i][:12], 1, 2)

Дает вывод как enter image description here

Пока

s = [
 u'abcdef',
 u'akash',
 u'rohit',
 u'anubhav',
 u'bhargav',
 u'achut',
 u'punnet',
 u'tom',
 u'rach',
 u'kamal'
 ]
for i in range(0, 10):
     print form.format(s[i][:12], 1, 2)

Дает: enter image description here

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

Вы можете использовать модуль wcwidth, он преодолевает проблемы, когда длина вкладки интерпретируется по-разному в различных терминалах (насколько я знаю).

Я использовал Python 3 здесь, я так понимаю, выиспользуя 2, так что ваш пробег может отличаться.Кроме того, я изменил форматирование вашего вывода, чтобы продемонстрировать некоторые используемые переменные

Решение

from wcwidth import wcswidth

a = [
    u'സി ട്രീമിം',
    u'ബി ഡോഗേറ്റ്',
    u'ജെ ഹോളണ്ട്',
    u'എം നസീർ ',
    u'എം ബസ്ചാഗൻ…',
    u'ടി ഹെഡ് ',
    u'കെ ഭാരത് ',
    u'എം സിറാജ് ',
    u'എ ഈശ്വരൻ ',
    u'സി ഹാൻഡ്‌സ്‌കോംബ് ബി'
]

desired = 15
max_str = 12

for item in a:

    sub_str = item[:max_str]

    diff = len(sub_str) - wcswidth(sub_str)

    indent = desired + diff if desired - wcswidth(sub_str) > 0 else desired + diff - 1

    form = u'{:<'+ str(indent) +'} {:<3}{:<3}{:<3}'

    print (form.format(sub_str, len(sub_str), wcswidth(sub_str), indent))

Результат:

enter image description here

0 голосов
/ 22 октября 2018

Вы печатаете Кодовые точки Unicode малаялам , в которых используется множество знаков гласных для изменения предыдущего глифа.Эти кодовые точки с гласными знаками, которые сами по себе не образуют новую букву, и малаялам не выдают в терминале ту же обычную ширину вывода, что и буквы ASCII.

Например, ваша первая строка начинается с U + 0D38 МАЛАЯЛАМ ПИСЬМО SA и U + 0D3F МАЛАЯЛАМ ГЛАГОЛЬНЫЙ ЗНАК I .Первый, буква SA , занимает полную позицию на экране, а второй символ, знак гласного I , перед SA, изменяет способ печати буквы.Обратите внимание, что при печати 2 кодовых точек имеется только один видимый глиф:

>>> print u'\u0d38'  # letter SA
സ
>>> print u'\u0d3f'  # vowel sign I
 ി
>>> print u'\u0d38\u0d3f'  # both together
സി

Ширина кодовых точек малаялам также различна;если вы добавляете буквы ASCII ниже SA и гласный знак I, отдельно и вместе, это выглядит следующим образом:

>>> print u'\u0d38\nA..\n\u0d3f\nB..\n\u0d38\u0d3f\nAB.'  # with ASCII letters for size
സ
A..
 ി
B..
സി
AB.

Обратите внимание, что шире A (примерно в 2,5 раза шире), аസി почти равно 3 кодовым точкам ASCII фиксированной ширины!Однако не все малаяламские буквы такие широкие.Следующая буква в первом примере это U + 0D1F МАЛАЯЛАМСКОЕ ПИСЬМО TTA , которое гораздо менее широко:

>>> print u'\u0d38\nA..\n\u0d1f\nB..'
സ
A..
ട
B..

На практике я надеюсь, что разница не имеет значенияи вместо этого кодовые точки объединяются так, что вывод заканчивается примерно одинаковой ширины.

Далее, у малаялама есть и другие комбинирующие символы;Ваша первая строка имеет U + 0D4D ЗНАК МАЛАЯЛАМ ВИРАМА , который был объединен с предыдущей буквой TTA.

Диакритические знаки, в сочетании с предыдущей буквой, приводят к хаосу с шириной печати:

>>> print u'\u0d1f\nA..\n\u0d4d\nB..\n\u0d1f\u0d4d\nAB.'
ട
A..
 ്
B..
ട്
AB.

Буква TTA такая же широкая, как и буква ASCII, и когда вы добавляете знак virama, ширина фактически не меняется.

Вы можете приблизить размеры, посмотрев накодовая точка Unicode общие категории .Функция unicodedata.category() дает вам категорию в виде строки:

>>> import unicodedata
>>> unicodedata.category(u'\u0d38')
'Lo'
>>> unicodedata.category(u'\u0d3f')
'Mc'
>>> unicodedata.category(u'\u0d4d')
'Mn'

Буква SA - это Lo (Буква, другая), знак гласной - Mc (Отметка(объединение пробелов), и знак virama равен Mn (Марк, без пробелов).

>>> categories = {}
>>> for c in a[0]:
...     cat = unicodedata.category(c)
...     categories[cat] = categories.get(cat, 0) + 1
... 
>>> categories
{'Lo': 4, 'Mn': 1, 'Mc': 4, 'Zs': 1}

Итак, для первой строки есть 4 буквы, 4 знака объединения и один знак гласного.Категория Zs (Разделитель, пробел) предназначена для символа пробела ' ' ASCII.

Можем ли мы получить лучшую предсказуемую ширину, если пропустить символы Mc и Mn?Строка a[0] будет иметь ширину 5 символов (4 раза Lo и 1 пробел):

>>> print a[0] + '\nABCDE.'
സി ട്രീമിം
ABCDE.

В браузере это выглядит недостаточно близко, но в моем окне терминала iTerm это выглядит такэто:

imageസി ട്രീമിം and ABCDE., with the capital letters in the second string producing roughly the same width on the screen as the first line.">

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

import unicodedata

def malayalam_width(s):
    return sum(1 for c in s if unicodedata.category(c)[0] != 'M')

form = u'{:<{width}}{:<3}({})'
for line in a:
    line = line[:12]
    adjust = len(line) - malayalam_width(line)
    print form.format(line, 1, 2, width=15 + adjust)

Это улучшает вывод много уже:

Output on terminal with adjusted code; columns line up better but still too far apart

Кажется, что более широкие буквы все-таки имеют значение.Вы должны были бы вручную добавить дополнительную ширину для тех, чтобы получить лучший результат;с отображением буквы на скорректированную ширину вы можете сделать так, чтобы выровнять немного лучше.Однако ширина кодовой точки определяется шрифтом, который вы используете, и я не уверен, насколько легко найти шрифт, который использует одинаковую ширину для всех букв малаялам.

Я считаю, что гораздо проще использоватьтабуляция, используя

form = u'{:<{width}}\t{:<3}({})'
for line in a:
    line = line[:12]
    adjust = len(line) - malayalam_width(line)
    print form.format(line, 1, 2, width=12 + adjust)

Теперь числа выстраиваются в линию:

Lined up columns with tabs

Вам необходимо продолжать регулировку ширины;в противном случае вы окажетесь в неправильной табуляции и остановитесь в половине случаев.

Предостережение: я совсем не знаком со сценарием малаялам, и я уверен, что пропустил тонкости о том, как различные буквы, гласные знакии диакритические знаки взаимодействуют.Кто-то, кто более знаком со сценарием и кодовыми точками Unicode, вероятно, сможет создать лучшую функцию приближения ширины, чем я представил здесь.

Я также проигнорировал кодовые точки 2 U + 200C ZERO WIDTH NON-JOINER , которые в настоящее время присутствуют в вашей последней строке;Вы можете удалить их из своих данных.Как следует из названия, он также не имеет ширины.

...