5

我正在使用 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
...

¨上面日志中的字符的值为 776,即“组合分音”。这是一个所谓的组合标记,属于字素,或者更确切地说属于组合变音符号。所以这一切都是有道理的,但我不明白是什么软件组件将这两个字符组合成一个变音符号,以及在哪里指定了这种行为。

  • 这与强大的字符代码表使用几个字节作为内部表示这一事实无关。几个字节与两个组合字符不同。
  • 任何简单print()的字符串都会向我显示组合字符,因此它既不是上面的某个 UI 层。
  • 我记得用 PHP 也观察到了这一点。我想任何现代语言都可以处理这个问题。

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

Java是否有一种规范化方法,可以使组合代码点的单个代码点像这里一样?将有助于使用正则表达式...

非常感谢任何提示。

4

1 回答 1

6

答案一:规范与责任

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

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

顺便说一句,如果你用 Combining Diaeresis 做一些实验,请注意还有一个“非功能性”代码 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
...
于 2015-11-04T10:34:16.017 回答