以Unicode的不同编码为例 UTF-16le 或者 UTF-8, ,一个字符可能占用2或3个字节。许多 Unicode 应用程序并不关心 Unicode 字符的显示宽度,就像它们都是拉丁字母一样。例如,在 80- 列文本,应包含 40 汉字或 80 拉丁字母在一行中,但大多数应用程序(如 Eclipse、Notepad++ 和所有知名的文本编辑器,我敢说是否有什么好的例外)只是将每个汉字视为拉丁字母的 1 个宽度。这肯定会使结果格式变得丑陋且不对齐。

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

apple   10
banana  7
苹果      6
猕猴桃     31
pear    16

然而,预期的格式是(每个汉字按2个宽度计算):

apple   10
banana  7
苹果    6
猕猴桃  31
pear    16

对字符显示宽度的不正确计算使得这些编辑器在进行制表符对齐、换行和段落重新格式化时完全无用。

虽然不同字体之间的字符宽度可能有所不同,但在所有固定大小终端字体的情况下,汉字始终是双倍宽度。也就是说,无论字体如何,每个汉字都优选以2宽度显示。

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

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

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

所以,Unicode 代码点的显示宽度可能不是整数,我认为没关系,它们在实践中可以接地为整数,至少比没有好。

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

有帮助吗?

解决方案

听起来您正在寻找类似的东西 wcwidthwcswidth, ,在IEEE STD 1003.1-2001中定义,但从ISO C中删除

wcwidth() 功能应确定宽字符所需的列位置数量 厕所. 。这 wcwidth() 功能应返回0(如果 厕所 是零字符代码),或返回宽字符代码所占用的列位置的数量 厕所, ,或返回-1(如果 厕所 与可打印的宽字符代码相对应。

马库斯·库恩(Markus Kuhn)写了一个开源版本, wcwidth.c, ,基于Unicode 5.0。它包括对问题的描述,以及该地区缺乏标准的确认:

在固定宽度的输出设备中,拉丁字符都占据了一个相等宽度的单个“单元格”位置,而意识形态CJK字符占据了两个这样的单元格。使用UTF-8编码的终端线应用程序和(电视式风格)字符终端之间的互操作性要求达成一致性,即在哪个字符上应通过数量来推进光标。目前尚无确立的形式标准,Unicode特征应占据角色终端上有多少个细胞位置。这些例程是根据应用于Unicode财团提供的数据的简单规则来定义此类行为的首次尝试。 [...

它实施以下规则:

  • 空字符(u+0000)的列宽度为0。
  • 其他C0/C1控制字符和DEL将导致返回值为-1。
  • 非间隔和封闭组合字符(Unicode数据库中的一般类别代码MN或我)的列宽度为0。
  • 软连字符(U+00AD)的列宽度为1。
  • 其他格式字符(Unicode数据库中的一般类别代码CF)和零宽度空间(U+200B)的列宽度为0。
  • Hangul Jamo内侧元音和最终辅音(U+1160-U+11FF)的列宽度为0。
  • Unicode技术报告中定义的东亚宽度(W)或东亚全宽(F)类别中的间距性字符的列宽度为2。
  • 所有其余字符(包括所有可打印的ISO 8859-1和WGL4字符,Unicode控制字符等)的列宽度为1。

其他提示

您会混淆代码点,绘图和编码。

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

素图是“我们认为是角色的内容”,这些是显示的内容。一个代码点(例如拉丁较低案例a),但在其他情况下可能需要多个代码点(例如,拉丁语较低案例A,结合急性并结合下划线以将较低的情况与急性和下划线相结合, 夸瓦拉)。在某些情况下,有多种代码点组合来创建相同的素数(例如,急性和结合下划线的拉丁语下部案例A),这是“归一化”,

IE单素的编码长度将取决于编码和归一化。

素数的显示宽度将取决于与编码长度独立于字体,样式和大小。

有关更多信息,请参见Wikipedia UnicodeUnicode的家. 。还有一些出色的书,也许最著名的书”字体和编码“ Yannis Haralambous,O'Reilly。

反映这一概念的Unicode属性是 East_asian_width. 。在一般Unicode渲染的背景下,它并不是真正可靠的视觉宽度,因为非亚洲字符,字符等组合也不会在单独的字体中排队。 (您的示例当然不会为我排队。)

Java没有内置的能力来阅读该字符的属性(尽管 Android的扩展 做)。你可以从 ICU4J 如果您真的需要它。

我相信要正确执行此操作,您需要考虑已发布的 Unicode 标准的组件,称为 Unicode 标准附件#14, Unicode 换行算法.

如果你用 Perl 编程,你想知道的事情会非常简单,因为 Perl 的 Unicode::换行符 实现 UAX#14 的模块包含一个带有简单的类 columns 方法告诉您其字符串参数的正确答案。这些东西在亚洲语言上尤其有效,在亚洲语言中绝对没有其他方法可以做到这一点。该模块包含 6,000 多个单元测试,得到积极维护,其作者本人就是亚洲人,因此对他来说,确保这些棘手的部分完全正确非常重要。

该模块的大部分内容都是用 C 编写的库。我还没有研究如何从 Perl 等其他语言调用其组件 C 库,但您可能会考虑这是否可行。

关于“或任何Java库函数来计算显示宽度?”:如果有一个我从未找到它。

对字符 /字符串的宽度的最简单方法是将其写入GNU Unicode字体( http://unifoundry.com/unifont.html )并测量字符宽度。不干净,但是到目前为止,它适用于我能想到的每个编码。

fwiw这是我要做的:

java.awt.font.Font MONOSPACEFONT = Font.createFont(Font.TRUETYPE_FONT, 
    new File("unifont-5.1.20080907.ttf"));

java.awt.font.FontRenderContext FRC = new FontRenderContext(null, true, true);

int charWidth =  (int) (2.0*((java.awt.geom.Rectangle2D.Float) 
    MONOSPACEFONT.getStringBounds(stringToMeasure, FRC)).width);

...这几乎应该在您部署JVM的任何地方工作(在无头环境中运行良好)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top