java - 双角字符的 String.format

标签 java unicode string-formatting cjk

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 Technical Report titled "East Asian Width":

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 有一个 very helpful site for evaluating their fonts ,它允许您:

  • 按语言过滤字体:日语、中文等。
  • 查看正在渲染的大量字符。例如 this page for Noto Sans JP 显示:
    • 日语字形比拉丁字形更宽。
    • 日语字形是固定宽度的,而拉丁字形不是。
  • 输入您想要的任何文本,并将其应用于所有选定的字体以进行比较。例如,此屏幕截图显示了 AEIOUY 的拉丁字形与一些使用不同字体的日语字形的外观。请注意,拉丁字形的宽度始终较小,尽管数量不同,具体取决于所使用的字体和要呈现的特定字形:

    asianLatinFonts

以下是对齐问题的可能解决方案:

  • 使用 Kosugi Maru 字体(上面屏幕截图中顶行的中间)时,日语字符的宽度似乎是拉丁字符的两倍,因此请使用该字体来渲染输出。
  • 渲染格式化文本时,每个要显示的日语字符的前导空格必须减少 1,以确保列对齐(因为日语字形的宽度是其两倍)。

因此,在代码中,将前导空格的数量减少为要呈现的日语字形的数量:

    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!

这是在 Windows 10 上的 NetBeans 中运行该代码的输出,显示了正确对齐的列:

asiaFonts

注释:

关于java - 双角字符的 String.format,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51464345/

相关文章:

java.lang.UnsupportedClassVersionError : Bad version number in . 类文件

java - 使用 MySQL 的自动增量并需要检索该数字

objective-c - 使用 NFD unicode 的 NSString 规范化

php - 从十六进制代码创建 UTF-8 字符串

java - 将java语法折叠成单行

ios - 基于 Iphone 电话号码格式化语言环境

java - 包含上下文不同的文件 当 HTML 文件位于不同的 servlet 上下文中时,如何将 HTML 文件包含在我的 JSP 文件中?

python - 如何本地化我的 WTForms 验证消息?

java - float 的格式

java - Spring 数据 JDBC : Conversion of compound domain types