Как узнать предпочтительную ширину экрана (в столбцах) символов Unicode?

В разных кодировках Юникода, например UTF-16le или UTF-8, символ может занимать 2 или 3 байта. Многие приложения Unicode не заботятся о ширине отображения символов Unicode, так же как и все латинские буквы. Например, в тексте 80 -column, который должен содержать китайские символы 40 или 80 латинские буквы в одной строке, но большинство приложений (например, Eclipse, Notepad ++ и все известные текстовые редакторы, я смею, если есть какое-то хорошее исключение) просто пересчитайте каждый китайский символ как 1 ширину как латинскую букву. Это, безусловно, делает формат результата уродливым и неприсоединенным.

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

apple   10
banana  7
苹果      6
猕猴桃     31
pear    16

Однако ожидаемый формат (подсчет каждого китайского символа как 2 ширины):

apple   10
banana  7
苹果    6
猕猴桃  31
pear    16

Неправильный расчет ширины экрана символов делает эти редакторы совершенно бесполезными при выполнении выравнивания табуляции, а также обертывание строк и форматирование абзаца.

Хотя ширина символа может варьироваться между разными шрифтами, но во всех случаях шрифта терминала фиксированного размера китайский символ всегда имеет двойную ширину. То есть, несмотря на шрифт, каждый китайский символ предпочтительнее отображать в 2 ширины.

Одно из решений: я могу получить правильную ширину, преобразовывая кодировку в GB2312, в GB2312, каждый китайский символ занимает 2 байта. однако некоторые символы Unicode не существуют в кодировке GB2312 (или GBK). И, вообще говоря, не рекомендуется вычислять ширину экрана из закодированного размера в байтах.

Просто вычислить весь символ в Юникоде в диапазоне (\u0080.. \uFFFF), так как 2-кратная также неверна, потому что в этом диапазоне также много символов ширины 1 ширины.

Также трудно вычислить ширину отображения арабских букв и корейских букв, поскольку они строят слово/символ произвольным числом кодовых точек Unicode.

Таким образом, ширина отображения кода кода Unicode может быть не целочисленным, я считаю, что это нормально, их можно заземлить на целые на практике, по крайней мере, лучше, чем ни один.

Итак, есть ли какой-либо атрибут, связанный с предпочтительной шириной экрана char в стандарте Unicode? Или любую библиотечную функцию Java для вычисления ширины экрана?

Ответ 1

Похоже, вы ищете что-то вроде wcwidth и wcswidth, определенный в IEEE Std 1003.1-2001, но удаленный из ISO C:

Функция wcwidth() должна определять количество позиций столбцов требуется для широкого символа wc. Функция wcwidth() должна либо вернуть 0 (если wc - нулевой широкосимвольный код), либо вернуть количество позиций столбцов, которое должно быть занято широкосимвольным кодом wc или return -1 (если wc не соответствует печатаемому широкоформатный код).

Маркус Кун написал версию с открытым исходным кодом wcwidth.c на основе Unicode 5.0. Он включает описание проблемы и подтверждение отсутствия стандартов в этой области:

В устройствах с фиксированной шириной латинские символы занимают одно "ячейковая" позиция равной ширины, тогда как идеографические символы CJK занимают две такие ячейки. Взаимодействие между терминальной линией приложений и (телетайпов) символьных терминалов с использованием UTF-8 кодирование требует согласия, по которому персонаж должен продвигать курсор, сколько позиций ячейки. Нет установленных формальных стандартов существуют в настоящее время, на которых символ Юникода будет занимать количество ячеек позиции на символьных терминалах. Эти подпрограммы являются первой попыткой определения такого поведения на основе простых правил, применяемых к данным предоставляемых Консорциумом Юникод. [...]

Он реализует следующие правила:

  • Нулевой символ (U + 0000) имеет ширину столбца 0.
  • Другие управляющие символы C0/C1 и DEL приведут к возврату -1.
  • Непересекающиеся и охватывающие комбинирующие символы (код общей категории Mn или Me в базе данных Юникода) имеют ширину столбца 0.
  • SOFT HYPHEN (U + 00AD) имеет ширину столбца 1.
  • Другие символы формата (код общей категории Cf в базе данных Юникода) и ZERO WIDTH SPACE (U + 200B) имеют ширину столбца 0.
  • Медианные гласные Hangul Jamo и конечные согласные (U + 1160-U + 11FF) имеют ширину столбца 0.
  • Символы интервала в Восточной Азии (W) или восточноазиатской категории полной ширины (F), как определено в Техническом отчете № 11 Юникода, имеют ширину столбца 2.
  • Все остальные символы (включая все печатные символы ISO 8859-1 и WGL4, символы управления Unicode и т.д.) имеют ширину столбца 1.

Ответ 2

Вы вводите в заблуждение кодовые точки, графемы и кодировку.

Кодирование - это то, как кодовые точки преобразуются в поток октетов для хранения, передачи или обработки. Оба UTF-8 и UTF-16 представляют собой кодировки с переменной шириной, причем разные кодовые точки нуждаются в другом количестве октетов (для UTF-8 - от 1 до, IIRC, 6 и UTF-16 - от 2 до 4).

Графемы - это "то, что мы видим как символ", это то, что отображается. Одна кодовая точка (например, LATIN LOWER CASE A) для одной графемы, но в других случаях может потребоваться несколько кодовых точек (например, LATIN LOWER CASE A, КОМБИНИРОВАННЫЕ ОСТРОМ и КОМБИНИРОВАННЫМ ПОДРОБНЫМ, чтобы получить нижний регистр с острым и подчеркиванием, используемый в Kwakwala). В некоторых случаях существует более одной комбинации кодовых точек для создания одной и той же графемы (например, LATIN LOWER CASE A WITH ONE и COMBINING UNDERSCORE), это "нормализация",

т.е. длина кодирования одной графемы будет зависеть от кодирования и нормализации.

Ширина экрана графемы будет зависеть от шрифта, стиля и размера независимо от длины кодирования.

Для получения дополнительной информации см. Wikipedia на Unicode и Unicode home. Есть также отличные книги, возможно, в первую очередь "Шрифты и кодировки" Яниса Хараламбуса, О'Рейли.

Ответ 3

Свойство Unicode, отражающее эту концепцию, East_Asian_Width. Он не очень надежный, как визуальная ширина в контексте общего рендеринга Unicode, так как неазиатские символы, сочетающие символы и т.д., Не будут выстраиваться даже в моноширинном шрифте. (Ваш пример, конечно, не отображает меня для меня.)

Java не имеет встроенной способности читать это свойство для символов (хотя расширение для Android). Вы можете получить его из ICU4J, если вам это действительно нужно.

Ответ 4

Я считаю, что для правильной работы вам необходимо рассмотреть этот компонент опубликованного стандарта Unicode, известный как Стандартное приложение Unicode № 14, Unicode Линейный алгоритм.

Если вы программировали на Perl, то, что вы хотите знать, было бы очень просто, потому что модуль Perls Unicode:: LineBreak, реализующий UAX # 14 включает класс с простым методом columns, который сообщает вам правильный ответ для его строкового аргумента. Эти вещи особенно хорошо работают на азиатских языках, где absolutley ничего не сделает. Этот модуль включает более 6000 модульных тестов, он активно поддерживается, и его автор сам является азиатским, поэтому важно, чтобы эти хитрые биты были точными.

Большая часть кистей модуля - это библиотека, написанная на C. Я не смотрел, как вызвать библиотеку своего компонента C с других языков на Perl, но вы можете посмотреть, возможно ли это.

Ответ 5

Что касается "или любой функции библиотеки Java для вычисления ширины экрана?": если есть, я ее никогда не нашел.

Самый простой способ вычисления ширины символа/строки - записать его в шрифт Unicode GNU (http://unifoundry.com/unifont.html) и измерить ширина символа. Не чистый, но до сих пор он работал для каждой кодировки, о которой я могу думать.

FWIW вот что я делаю:

java.awt.font.Font MONOSPACEFONT = Font.createFont(Font.TRUETYPE_FONT, 
    new File("unifont-5.1.20080907.ttf"));

java.awt.font.FontRenderContext FRC = new FontRenderContext(null, true, true);

int charWidth =  (int) (2.0*((java.awt.geom.Rectangle2D.Float) 
    MONOSPACEFONT.getStringBounds(stringToMeasure, FRC)).width);

... это должно работать практически везде, где вы развертываете свою JVM (она отлично работает в безголовой среде).