我正在使用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,即“结合分音符”。这是一个所谓的组合标记,属于graphemes,或者更准确地说属于combining diacritics。所有这一切都有道理,但我不明白哪个软件组件将两个字符组合到一个变音符号,以及指定此行为的位置。
print()
都会显示组合字符,因此它不是上面的某个UI层。 什么组件导致组合字符显示为单个组合字符?这一切有多可靠?
Java是一种规范化方法,可以生成组合代码点的单个代码点,例如here吗?对使用正则表达式有帮助...
非常感谢任何暗示。
答案 0 :(得分:6)
答案1:规范和责任
您描述的行为在Unicode Standard Annex #15, Unicode Normalization Forms中定义。这是关于组合字符和单个代码点以及代码点分解的等价性。除德语之外的许多语言都严重依赖于构成字素。
Java内部将字符串表示为UTF-16。因此,它的String
类所做的就是将UTF-16代码链提供给其他组件。由周围的软件(例如任何类型的文本视图组件)来正确地组合链。你感觉到这一点,例如正则表达式将您的合并ö
分开,但在某些视图中显示正确。
顺便说一句,如果你使用Combining Diaeresis进行一些实验,请注意还有一个非功能性的"代码168,这是一个简单的ASCII字符,称为" Spacing Diaeresis"。代码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
...