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? Если нет, то есть ли альтернативный метод или библиотека, которые могут сделать это правильно?
Не бывает символа двойной ширины. Ширина символа зависит от используемого шрифта. И в некотором роде (когда есть кернинг) соседние символы.




Нет проблем с 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 есть очень полезный сайт для оценки своих шрифтов, который позволяет:
Введите любой желаемый текст и примените его ко всем выбранным шрифтам для сравнения. Например, на этом снимке экрана показано, как латинские глифы для AEIOUY выглядят рядом с некоторыми японскими глифами с использованием разных шрифтов. Обратите внимание, что ширина латинских глифов всегда меньше, хотя и в разной степени, в зависимости от используемого шрифта и конкретного глифа, который нужно отобразить:
Вот возможное решение вашей проблемы с выравниванием:
Итак, в коде уменьшите количество ведущих пробелов на количество японских глифов, которые нужно отобразить:
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, показывающий, что столбцы правильно выровнены:
Заметки:
Я согласен, что это не так, но я не уверен, что это должно быть. Ваша потребность отличается от ее цели. Я бы сказал, что выравнивание должно основываться на подсчете графем. Но это то, что написано в документации: «символ» (он же
char; он же кодовая единица UTF-16). Возможно, вам придется свернуть свой собственный.