如何知道Unicode字符的首选显示宽度(以列为单位)?

Xiè*_*léi 16 unicode text-formatting mbcs character-properties

在Unicode的不同编码中,例如UTF-16leUTF-8,字符可能占用2或3个字节.许多Unicode应用程序不像处理所有拉丁字母那样处理Unicode字符的显示宽度.例如,在80列文本中,一行应包含40个中文字符或80个拉丁字母,但大多数应用程序(如Eclipse,Notepad ++和所有知名文本编辑器,我敢于有任何好的例外)只计算每个汉字作为拉丁字母的1宽度.这肯定会使结果格式变得丑陋且不对齐.

例如,制表符宽度为8将得到以下难看的结果(将所有Unicode计为1个显示宽度):

apple   10
banana  7
??      6
???     31
pear    16
Run Code Online (Sandbox Code Playgroud)

但是,预期的格式是(将每个汉字计为2个宽度):

apple   10
banana  7
??    6
???  31
pear    16
Run Code Online (Sandbox Code Playgroud)

对字符显示宽度的不正确计算使得这些编辑器在进行制表对齐,换行和段重组时完全没用.

虽然,字符的宽度可能会因不同的字体而异,但在固定大小的终端字体的所有情况下,汉字始终是双倍宽度.也就是说,尽管有字体,但每个汉字最好以2宽度显示.

解决方法之一是,我可以通过将编码转换为GB2312来获得正确的宽度,在GB2312编码中每个汉字占用2个字节.但是,GB2312字符集(或GBK字符集)中不存在某些Unicode字符.而且,一般来说,从编码大小(以字节为单位)计算显示宽度并不是一个好主意.

简单地计算(\u0080... \uFFFF)范围内Unicode中的所有字符,因为2宽度也不正确,因为在该范围内还散布着许多1宽度字符.

计算阿拉伯字母和韩文字母的显示宽度时也很困难,因为它们通过任意数量的Unicode代码点构造一个字/字符.

因此,Unicode代码点的显示宽度可能不是整数,我认为没关系,它们可以在实践中基于整数,至少比没有好.

那么,在Unicode标准中是否有与char的首选显示宽度相关的属性?或者任何Java库函数来计算显示宽度?

Tre*_*son 21

听起来你正在寻找的东西像wcwidthwcswidth在IEEE标准1003.1-2001定义,但是从ISO C删除:

wcwidth()函数应确定宽字符wc所需的列位置数.该wcwidth()函数应返回0(如果wc是空宽字符代码),或返回宽字符代码wc占用的列位置数 ,或返回-1(如果wc不对应于可打印的宽) - 字符代码).

Markus Kuhn编写了一个基于Unicode 5.0的开源版本wcwidth.c.它包括对问题的描述,以及对该领域缺乏标准的承认:

在固定宽度输出设备中,拉丁字符全部占据相等宽度的单个"单元"位置,而表意CJK字符占据两个这样的单元.终端线应用程序和使用UTF-8编码的(电传打字机式)字符终端之间的互操作性要求就哪个字符应该将光标前进多少单元格位置达成一致.目前还没有既定的正式标准,其中Unicode字符应占据字符终端上的单元位置数.这些例程是基于应用于Unicode Consortium提供的数据的简单规则来定义此类行为的第一次尝试.[...]

它实现了以下规则:

  • 空字符(U + 0000)的列宽为0.
  • 其他C0/C1控制字符和DEL将导致返回值为-1.
  • 非间距和封闭组合字符(Unicode数据库中的常规类别代码Mn或Me)的列宽为0.
  • SOFT HYPHEN(U + 00AD)的列宽为1.
  • 其他格式字符(Unicode数据库中的常规类别代码Cf)和ZERO WIDTH SPACE(U + 200B)的列宽为0.
  • Hangul Jamo内侧元音和最终辅音(U + 1160-U + 11FF)的列宽为0.
  • Unicode技术报告#11中定义的东亚宽(W)或东亚全宽(F)类别中的间距字符的列宽为2.
  • 所有剩余字符(包括所有可打印的ISO 8859-1和WGL4字符,Unicode控制字符等)的列宽为1.

  • 自 Unicode 5.2 (2009) 起,还有另一个范围,其中包含 Hangul Jamo 中元音和尾辅音 U+D7B0..U+D7FF。 (2认同)

Ric*_*ard 7

您混淆了代码点、字素和编码。

编码是指如何将代码点转换为八位字节流以进行存储、传输或处理。UTF-8 和 UTF-16 都是可变宽度编码,不同的代码点需要不同数量的八位字节(对于 UTF-8,从 1 到 IIRC、6,UTF-16 为 2 或 4)。

字素是“我们所看到的字符”,这些是显示的内容。一个字素对应一个代码点(例如 LATIN LOWER CASE A),但在其他情况下可能需要多个代码点(例如 LATIN LOWER CASE A、组合急性符号和组合下划线以获得Kwakwala中使用的带有急性符号和下划线的小写字母) 。在某些情况下,有不止一种代码点组合来创建相同的字素(例如拉丁文小写 A 与尖音符号和组合下划线),这就是“标准化”,

即,单个字素的编码长度将取决于编码和标准化。

字素的显示宽度将取决于字体、样式和大小,与编码长度无关。

有关详细信息,请参阅有关Unicode的 Wikipedia和Unicode 的主页。还有一些优秀的书籍,其中最著名的可能是 Yannis Haralambous、O'Reilly 所著的《字体与编码》。


bob*_*nce 6

反映此概念的 Unicode 属性是East_Asian_Width。在一般 Unicode 渲染的上下文中,它作为视觉宽度并不真正可靠,因为非亚洲字符、组合字符等即使在等宽字体中也无法对齐。(你的例子当然不会为我呈现排队。)

Java 没有内置的功能来读取此属性的字符(尽管Android 的扩展可以)。如果您确实需要,可以从ICU4J获取。