LINUX.ORG.RU

Как определить ширину символа?

 ,


1

4

Задача: определить ширину текста (в колонках) перед его выводом на линуксовый терминал.

Планирую определить ширину строки как сумму ширин входящих в строку символов.
Каждый символ может занимать 0, 1 или 2 знакоместа:

  • 0 - непечатаемые кодепоинты, а также кодепоинты, добавляющие какую-нибудь закорючку к предыдущему символу
  • 1 - обычные символы, напр, латинские или русские буквы
  • 2 - напр, китайские иероглифы, занимающие два знакоместа

Решение должно быть изолированным (т.е., не должно зависеть от текущих настроек эмулятора терминала, в котором работает скрипт). Если случится такое, что в разных моноширинных шрифтах какой-то символ имеет то обычную, то двойную ширину, то можно выбрать любую ширину для этого символа: и 1, и 2.

Как отличить символы обычной ширины от двойной ширины, я нашёл: нужно посмотреть свойство EastAsianWidth в Unicode Character Database

А как понять, что символ нулевой ширины (т.е., не увеличивает ширину строки)?
Будет ли достаточно смотреть только на название категории символа без учёта доп. свойств символа?

Список всех категорий

C  = "Other",
Cc = "Control",
Cf = "Format",
Cn = "Unassigned",
Co = "Private_Use",
Cs = "Surrogate",
L  = "Letter",
LC = "Cased_Letter",
Ll = "Lowercase_Letter",
Lm = "Modifier_Letter",
Lo = "Other_Letter",
Lt = "Titlecase_Letter",
Lu = "Uppercase_Letter",
M  = "Mark",
Mc = "Spacing_Mark",
Me = "Enclosing_Mark",
Mn = "Nonspacing_Mark",
N  = "Number",
Nd = "Decimal_Number",
Nl = "Letter_Number",
No = "Other_Number",
P  = "Punctuation",
Pc = "Connector_Punctuation",
Pd = "Dash_Punctuation",
Pe = "Close_Punctuation",
Pf = "Final_Punctuation",
Pi = "Initial_Punctuation",
Po = "Other_Punctuation",
Ps = "Open_Punctuation",
S  = "Symbol",
Sc = "Currency_Symbol",
Sk = "Modifier_Symbol",
Sm = "Math_Symbol",
So = "Other_Symbol",
Z  = "Separator",
Zl = "Line_Separator",
Zp = "Paragraph_Separator",
Zs = "Space_Separator",

Состав категорий


возьми логарифм числового значения символа. некоторые символы могут быть и 3 байта шириной. основание логарифма - не знаю, 16 наверное, или попробуй основани2е с каким-то преобразованием, поэкспериментируй.

anonymous ()
Ответ на: комментарий от RazrFalcon

У меня сомнение, что есть однозначный ответ на этот вопрос.

Вообще, все современные эмуляторы в той или иной мере реализуют хотя бы DEC VT52. Более редко используемые вещи, очевидно, реализованы не везде.

Я просто возразил, что даже при использовании шрифта заданной ширины можно определённые символы выводить на 2 или даже 4 знакоместа (но, разумеется, не в консоли).

ТС, кажется, хочет совсем другого.

Bass ★★★★★ ()

c++ и utf-8

@peregrine

c++ и utf-8 (комментарий) c++ и utf-8 (комментарий)

Костыль => Запускай терминал с профилем где выставлен твой моноширный шрифт в котором любой символ константной ширины гарантирован

Или все неугодные меняй на � мол нехера хероту вводить всякую =) типа П̛͇̘̤̪ͪ͞р̺̜̘͕̬̥̩̜͖ͬ͢о̧͓͍̘̝̘͙͉̆͌͊ͩ̀ͨ͠ͅч͙͎̥̬̫̱͔̒͐͛̔͆͐̓̚у̝̪̬͖̅̓ͣͤ͆̄̈̿̀͘в̸̜̹̪͚̝̆ͅс̊̐ͮ̇҉̛̻̱̙͕͈̗т̶̷̧̪̼͙̭̝̃̇̂̽̑̅в̨͉̮̳̥̯͔̮̽̐̆̉ͪ̿ͫ̋͠у̖̠̞̰͙̙͕͗̎̃͒͢͝й̛̛̪̬ͤ̎͗͂̐ͫ ͙͉̬̱͉̜̒̊̇ͯ̉ͧ͘г͖̬ͩ͘̕л͙̣͍̥̪͙̪̹̝̌̋ў̵̧̺̤̲̠̠͚̮ͭ̊б̷̸̙̹̮̼̠͉̰ͭ̏̇ӥ̵̤̥̱̘ͪ͂̾̽̚н̫̫͈̮ͨ̂͊̐̚у̸̗̹̮̮͖͙͈͆͗̾́͢ ̴͚͉̐͒ͩͯͪͨ̉ͦс̢̟̤̓͡в͉̳ͩ̇̔ͧо̠͍͋̉̊̾̎̂͜е̷̱͈̼͕̝͈͇ͪ̍͛͗ͨ̂͘й̸̨͇̟̜̘̝̠͈͕ͯ̈̔͂̂̐̉̾ ̴̱̼̯̬̬̹̘ͯ̀͂ͤͣ̓б̱͓̫͔ͮ̐о̙̜̜͙͈͇̱̹͕ͭ̅̏̔̚͠л͋͌͢͏̳̙͉̲̮̙͇и̼̲̬̙̙̟̮̐̅ͥͮ̃ͦ͗͋ͬ.͇͉̠̜̳̜̑ͭ̏̈̈́ͣП̛͇̘̤̪ͪ͞р̺̜̘͕̬̥̩̜͖ͬ͢о̧͓͍̘̝̘͙͉̆͌͊ͩ̀ͨ͠ͅч͙͎̥̬̫̱͔̒͐͛̔͆͐̓̚у̝̪̬͖̅̓ͣͤ͆̄̈̿̀͘в̸̜̹̪͚̆ͅс̊̐ͮ̇҉̛̻̱̙͕͈̗т̶̷̧̪̼͙̭̝̃̇̂̽̑̅в̨͉̮̳̥̯͔̮̽̐̆̉ͪ̿ͫ̋͠у̖̠̞̰͙̙͕͗̎̃͒͢͝й̛̛̪̬ͤ̎͗͂̐ͫ ͙͉̬̱͉̜̒̊̇ͯ̉ͧ͘г͖̬ͩ͘̕л͙̣͍̥̪͙̪̹̝̌̋ў̵̧̺̤̲̠̠͚̮ͭ̊б̷̸̙̹̮̼̠͉̰ͭ̏̇ӥ̵̤̥̱̘ͪ͂̾̽̚н̫̫͈̮ͨ̂͊̐̚у̸̗̹̮̮͖͙͈͆͗̾́͢ ̴͚͉̐͒ͩͯͪͨ̉ͦс̢̟̤̓͡в͉̳ͩ̇̔ͧо̠͍͋̉̊̾̎̂͜е̷̱͈̼͕̝͈͇ͪ̍͛͗ͨ̂͘й̸̨͇̟̜̘̝̠͈͕ͯ̈̔͂̂̐̉̾ ̴̱̼̯̬̬̹̘ͯ̀͂ͤͣ̓б̱͓̫͔ͮ̐о̙̜̜͙͈͇̱̹͕ͭ̅̏̔̚͠л͋͌͢͏̳̙͉̲̮̙͇и̼̲̬̙̙̟̮̐̅ͥͮ̃ͦ͗͋ͬ.͇͉̠̜̳̜̑ͭ̏̈̈́ͣ

LINUX-ORG-RU ★★ ()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

П̛͇̘̤̪ͪ͞р̺̜̘͕̬̥̩̜͖ͬ͢о̧͓͍̘̝̘͙͉̆͌͊ͩ̀ͨ͠ͅч͙͎̥̬̫̱͔̒͐͛̔͆͐̓̚у̝̪̬͖̅̓ͣͤ͆̄̈̿̀͘в̸̜̹̪͚̝̆ͅс̊̐ͮ̇҉̛̻̱̙͕͈̗т̶̷̧̪̼͙̭̝̃̇̂̽̑̅в̨͉̮̳̥̯͔̮̽̐̆̉ͪ̿ͫ̋͠у̖̠̞̰͙̙͕͗̎̃͒͢͝й̛̛̪̬ͤ̎͗͂̐ͫ ͙͉̬̱͉̜̒̊̇ͯ̉ͧ͘г͖̬ͩ͘̕л͙̣͍̥̪͙̪̹̝̌̋ў̵̧̺̤̲̠̠͚̮ͭ̊б̷̸̙̹̮̼̠͉̰ͭ̏̇ӥ̵̤̥̱̘ͪ͂̾̽̚н̫̫͈̮ͨ̂͊̐̚у̸̗̹̮̮͖͙͈͆͗̾́͢ ̴͚͉̐͒ͩͯͪͨ̉ͦс̢̟̤̓͡в͉̳ͩ̇̔ͧо̠͍͋̉̊̾̎̂͜е̷̱͈̼͕̝͈͇ͪ̍͛͗ͨ̂͘й̸̨͇̟̜̘̝̠͈͕ͯ̈̔͂̂̐̉̾ ̴̱̼̯̬̬̹̘ͯ̀͂ͤͣ̓б̱͓̫͔ͮ̐о̙̜̜͙͈͇̱̹͕ͭ̅̏̔̚͠л͋͌͢͏̳̙͉̲̮̙͇и̼̲̬̙̙̟̮̐̅ͥͮ̃ͦ͗͋ͬ.͇͉̠̜̳̜̑ͭ̏̈̈́ͣП̛͇̘̤̪ͪ͞р̺̜̘͕̬̥̩̜͖ͬ͢о̧͓͍̘̝̘͙͉̆͌͊ͩ̀ͨ͠ͅч͙͎̥̬̫̱͔̒͐͛̔͆͐̓̚у̝̪̬͖̅̓ͣͤ͆̄̈̿̀͘в̸̜̹̪͚̆ͅс̊̐ͮ̇҉̛̻̱̙͕͈̗т̶̷̧̪̼͙̭̝̃̇̂̽̑̅в̨͉̮̳̥̯͔̮̽̐̆̉ͪ̿ͫ̋͠у̖̠̞̰͙̙͕͗̎̃͒͢͝й̛̛̪̬ͤ̎͗͂̐ͫ ͙͉̬̱͉̜̒̊̇ͯ̉ͧ͘г͖̬ͩ͘̕л͙̣͍̥̪͙̪̹̝̌̋ў̵̧̺̤̲̠̠͚̮ͭ̊б̷̸̙̹̮̼̠͉̰ͭ̏̇ӥ̵̤̥̱̘ͪ͂̾̽̚н̫̫͈̮ͨ̂͊̐̚у̸̗̹̮̮͖͙͈͆͗̾́͢ ̴͚͉̐͒ͩͯͪͨ̉ͦс̢̟̤̓͡в͉̳ͩ̇̔ͧо̠͍͋̉̊̾̎̂͜е̷̱͈̼͕̝͈͇ͪ̍͛͗ͨ̂͘й̸̨͇̟̜̘̝̠͈͕ͯ̈̔͂̂̐̉̾ ̴̱̼̯̬̬̹̘ͯ̀͂ͤͣ̓б̱͓̫͔ͮ̐о̙̜̜͙͈͇̱̹͕ͭ̅̏̔̚͠л͋͌͢͏̳̙͉̲̮̙͇и̼̲̬̙̙̟̮̐̅ͥͮ̃ͦ͗͋ͬ.͇͉̠̜̳̜̑ͭ̏̈̈́ͣ

А чего? gnome-terminal вполне выводит этот текст моноширинным шрифтом, и закорючки отдельных позиций не занимают.

proud_anon ★★★★★ ()
Последнее исправление: proud_anon (всего исправлений: 1)

не должно зависеть от текущих настроек эмулятора терминала

А как понять, что символ нулевой ширины (т.е., не увеличивает ширину строки)?

Никак. И вообще, ты хочешь какую-то дичь.

no-such-file ★★★★★ ()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от xtouqh

спасибо за упоминание utf8proc
похоже, это актуальная версия того старого кода, который дал xaizek
передрал реализацию из utf8proc, проверил на фразе «прочувствуй свою боль», работает правильно ))
несмотря на гигантские таблицы, весь код wcwidth утоптался в 20 строк!

стандартная функция wcwidth() - с ошибками
напр, wcwidth(L'\u0524') возвращает -1, хотя это обычный символ
и ещё она почему-то зависит от локали, и в сишной локали не работает, превращаясь из-за этого в неюзабельную каку

Egor_ ()
Ответ на: комментарий от XoFfiCEr

все зависит от шрифта

все шрифты, используемые в терминале, как будто сговорились и единогласно поделили все символы на три множества: с шириной 0, 1 и 2 знакоместа
фактически получается так, что ширина символа в терминале от шрифта уже не зависит

Egor_ ()
Ответ на: комментарий от XoFfiCEr

это символ, который не добавляет дополнительной ширины к уже написанной ранее строке (напр, рисует закорючку над предыдущим символом или является управляющим неотображаемым кодом)

Egor_ ()