全角字符的String.format

时间:2018-07-22 10:53:29

标签: 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格式化全角字符的正确方法?如果不是,是否有其他方法或库能够正确执行此操作?

1 个答案:

答案 0 :(得分:0)

Java的String.format()没有问题,因为它无法“知道”您要如何呈现文本或将使用的字体。它的作用纯粹是组装格式化的文本字符串以随后显示。格式化文本的视觉外观(主要)由显示字体控制,开发人员必须相应地明确设置格式。

一种简单的解决方案是使用一种字体,该字体同时显示具有恒定宽度字形的拉丁字符和CJK字符,但我找不到。有关更多详细信息,请参见Unicode Technical Report titled "East Asian Width"

  

对于传统的东亚固定螺距字体,此宽度表示   调整为显示宽度的一半或整个单位宽度。普通的   该单位宽度的名称为“ Em”。虽然Em通常是   字母“ M”的高度,与东方的单位宽度相同   亚洲字体,因为在这些字体中,标准字符单元格为   广场。 相反,固定间距拉丁字体的字符宽度   例如Courier通常是Em的3/5。

我猜测可能没有任何等宽字体显示相同宽度的CJK字符和拉丁字符,只是因为它看起来很奇怪。例如,假设两个拉丁字符“ li”与两个日语字符“駆け”占据相同的宽度。因此,即使您使用等宽字体来呈现拉丁字符和CJK字符,尽管每种语言的字符都是等距的,但每种语言的宽度可能仍然不同。

Google有一个very helpful site for evaluating their fonts,可让您:

  • 按语言过滤字体:日语,中文等。
  • 查看正在渲染的大量字符。例如this page for 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!

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

asiaFonts

注意: