哪个组件处理字符串中的组合分音符?

What component handles a Combining Diaeresis in a string?

我正在处理 Java 中的文件名列表。

我观察到文件名中的一些单个字符,如 a、ö 和 ü 实际上由一个序列组成,您可以将其描述为两个单独的 ASCII 字符:

ö表示为o,¨

我通过 codePointAt() 检查看到了这一点。德国名字 "Rölli" 实际上是 "Ro¨lli":

...
20: R, 82
21: o, 111
22: ̈, 776
23: l, 108
24: l, 108
25: i, 105
...

上面日志中的字符¨value 776, which is a "Combining Diaeresis". This is a so called combining mark that belongs to the graphemes, or more precisely to the combining diacritics。所以这一切都是有道理的,但我不明白是什么软件组件将两个字符组合成一个变音符号,以及在哪里指定了这种行为。

什么因素导致组合字符显示为单个组合字符?这一切有多可靠?

是否有 Java 一种规范化方法,使组合代码点成为单个代码点,例如 here?对使用 Regex 有帮助...

非常感谢任何提示。

答案一:规范与责任

您描述的行为在 Unicode Standard Annex #15, Unicode Normalization Forms 中定义。这是关于组合字符和单个代码点的等效性以及代码点的分解。德语以外的许多语言严重依赖组合字素。

Java 在内部将字符串表示为 UTF-16。因此,它使用 String class 所做的一切都是将 UTF-16 代码链传递给其他组件。正确组合链取决于周围的软件(例如任何类型的文本视图组件)。你会在某些时刻感受到这一点,例如正则表达式将组合 ö 分开,但它在某些视图中显示正确。

顺便说一句,如果你用 Combining Diaeresis 做一些实验,请注意还有一个 "non-functional" 代码 168,这是一个简单的 ASCII 字符,称为 "Spacing Diaeresis"。 Code 168 不会导致任何软件将两个代码点合并为一个。为此,您需要 Unicode 776。

答案2:Javas归一化方法

基本上,您应该始终考虑组合字符 - 除非您确定您的数据源无法提供它们。最好先清理字符串。

寻找使用您的语言的 unicode 规范化方法,因为它们可以让您摆脱对单个 replace() 语句的摆弄,并且它们包含很多经验。

Java 有一个 Normalizer 处理组合字符的不同表示的对象:

https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.html

及其教程:https://docs.oracle.com/javase/tutorial/i18n/text/normalizerapi.html

因此调用此代码行后:

String normalized = Normalizer.normalize(someFileName, Normalizer.Form.NFC);

上述问题的日志打印如下所示:

...
19:  , 32
20: R, 82
21: ö, 246   <<< here were two combined chars before normalize()
22: l, 108
23: l, 108
24: i, 105
...