String.format для символов двойной ширины

Java String.format, похоже, не поддерживает символы двойной ширины, такие как японский или китайский:

System.out.println(String.format("%1$9s: %2$20s : %3$20s\n", "field", "expected", "actual"));
System.out.println(String.format("%1$9s: %2$20s : %3$20s\n", "surface", "駆け", "駆け"));

Вывод не выровнен правильно:

field:             expected :               actual
surface:                   駆け :                   駆け

Есть ли правильный способ форматирования символов двойной ширины с помощью String.format? Если нет, то есть ли альтернативный метод или библиотека, которые могут сделать это правильно?

Я согласен, что это не так, но я не уверен, что это должно быть. Ваша потребность отличается от ее цели. Я бы сказал, что выравнивание должно основываться на подсчете графем. Но это то, что написано в документации: «символ» (он же char; он же кодовая единица UTF-16). Возможно, вам придется свернуть свой собственный.

Tom Blodget 24.07.2018 02:15

Не бывает символа двойной ширины. Ширина символа зависит от используемого шрифта. И в некотором роде (когда есть кернинг) соседние символы.

Raedwald 26.12.2018 17:03
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
784
1

Ответы 1

Нет проблем с Java String.format(), поскольку он не может «знать», как вы хотите отобразить текст или шрифт, который будет использоваться. Его роль состоит исключительно в том, чтобы собрать отформатированную строку текста для последующего отображения. Внешний вид этого форматированного текста контролируется (в первую очередь) отображаемым шрифтом, и разработчик должен явно установить соответствующее форматирование.

Простым решением было бы использовать шрифт, который отображает символы латинского алфавита и CJK с глифами постоянной ширины, но я не смог его найти. Для получения более подробной информации см. Технический отчет Unicode под названием «Восточноазиатская ширина»:

For a traditional East Asian fixed pitch font, this width translates to a display width of either one half or a whole unit width. A common name for this unit width is “Em”. While an Em is customarily the height of the letter “M”, it is the same as the unit width in East Asian fonts, because in these fonts the standard character cell is square. In contrast, the character width for a fixed-pitch Latin font like Courier is generally 3/5 of an Em.

Я предполагаю, что не может быть моноширинного шрифта, отображающего символы CJK и латинские символы с одинаковой шириной просто потому, что это выглядело бы очень странно. Например, представьте, что два латинских символа «li» имеют ту же ширину, что и два японских символа «駆 け». Таким образом, даже если вы используете моноширинный шрифт для отображения как латинских символов, так и символов CJK, хотя символы для каждого языка являются моноширинными, ширина для каждого языка, вероятно, все еще различается.

У Google есть очень полезный сайт для оценки своих шрифтов, который позволяет:

  • Отфильтруйте шрифты по языку: японский, китайский и т. д.
  • Просмотр большого количества визуализируемых символов. Например, эта страница для Noto Sans JP показывает:
    • Японские глифы шире латинских.
    • У японских глифов фиксированная ширина, у латинских - нет.
  • Введите любой желаемый текст и примените его ко всем выбранным шрифтам для сравнения. Например, на этом снимке экрана показано, как латинские глифы для AEIOUY выглядят рядом с некоторыми японскими глифами с использованием разных шрифтов. Обратите внимание, что ширина латинских глифов всегда меньше, хотя и в разной степени, в зависимости от используемого шрифта и конкретного глифа, который нужно отобразить:

    asianLatinFonts

Вот возможное решение вашей проблемы с выравниванием:

  • При использовании шрифта Kosugi Maru (середина верхнего ряда на снимке экрана выше) японские символы кажутся ровно вдвое шире латинских символов, поэтому используйте этот шрифт для визуализации вывода.
  • При рендеринге форматированного текста ведущие пробелы должны быть уменьшены на один для каждого отображаемого японского символа, чтобы обеспечить выравнивание столбцов (поскольку японские глифы в два раза шире).

Итак, в коде уменьшите количество ведущих пробелов на количество японских глифов, которые нужно отобразить:

    System.out.println("* The display font is named MotoyaLMaru, created by installing Google font KosugiMaru-Regular.ttf.");
    System.out.println("* With this font Japanese glyphs seem to be twice the width of Latin glyphs.");
    System.out.println("* Downloaded from https://fonts.google.com/specimen/Kosugi+Maru?selection.family=Kosugi+Maru");
    System.out.println(" ");
    System.out.println(String.format("%1$9s: %2$20s : %3$20s\n", "field", "expected", "actual"));
    System.out.println(String.format("%1$9s: %2$18s : %3$18s\n", "surface", "駆け", "駆け")); // 18, not 20!
    System.out.println(String.format("%1$9s: %2$12s : %3$12s\n", "1234567", "川土空田天生花草", "川土空田天生花草")); // 12, not 20!

Это результат выполнения этого кода в NetBeans в Windows 10, показывающий, что столбцы правильно выровнены:

asiaFonts

Заметки:

Другие вопросы по теме