Unicode字符具有不对称的大/小写形式。为什么?

13
为什么以下三个字符的toLowertoUpper结果不对称
/**
  * Written in the Scala programming language, typed into the Scala REPL.
  * Results commented accordingly.
  */
/* Unicode Character 'LATIN CAPITAL LETTER SHARP S' (U+1E9E) */
'\u1e9e'.toHexString == "1e9e" // true
'\u1e9e'.toLower.toHexString == "df" // "df" == "df"
'\u1e9e'.toHexString == '\u1e9e'.toLower.toUpper.toHexString // "1e9e" != "df"
/* Unicode Character 'KELVIN SIGN' (U+212A) */
'\u212a'.toHexString == "212a" // "212a" == "212a"
'\u212a'.toLower.toHexString == "6b" // "6b" == "6b"
'\u212a'.toHexString == '\u212a'.toLower.toUpper.toHexString // "212a" != "4b"
/* Unicode Character 'LATIN CAPITAL LETTER I WITH DOT ABOVE' (U+0130) */
'\u0130'.toHexString == "130" // "130" == "130"
'\u0130'.toLower.toHexString == "69" // "69" == "69"
'\u0130'.toHexString == '\u0130'.toLower.toUpper.toHexString // "130" != "49"

3
或许是因为Unicode存在歧义性?某些字形在Unicode中有多种表示方式,而在toUpper或toLower之后进行相反操作将归一化到最“低”的代码点。 - user824425
Jeff Moser的博客文章土耳其测试详细介绍了土耳其语言环境下的问题。 - MPG
2个回答

13
首先,就第一个问题,有这个解释

在德语中,“Sharp S”(“ß”或U+00df)是小写字母,它的大写字母是“SS”。

换句话说,U+1E9E 变成小写字母 U+00DF,但是 U+00DF 的大写不是 U+1E9E。
对于第二个问题,U+212A(开尔文符号)变成小写字母 U+0068(拉丁小写字母 K)。U+0068 的大写是 U+004B(拉丁大写字母 K)。我觉得这个解释是有道理的。
至于第三个问题,U+0130(带点的大写拉丁字母 I)是土耳其和阿塞拜疆的字符,它小写后变为 U+0069(拉丁小写字母 i)。我想如果您在土耳其或阿塞拜疆本地的话,您会得到 U+0069 的正确的大写版本,但这可能并不普遍。
字符不一定具有对称的大写和小写转换。 编辑:回应PhiLho在下面的评论,Unicode 6.0规范提到了U+212A(开尔文符号):

有三个字母符号已经与常规字母给予规范等价性:U+2126欧姆符号、U+212A 开尔文符号和U+212B 埃斯特朗符号。在这三种情况下,应使用常规字母。如果根据Unicode标准附录#15“Unicode标准化形式”对文本进行了归一化处理,则这三个字符将被替换为其常规等效项。

换句话说,您不应该真正使用U+212A,而应该使用U+004B(大写拉丁字母K),如果您规范化您的Unicode文本,U+212A应该被替换为U+004B。


2
我认为将开尔文符号转换为小写是错误的,单位符号的大小写不应更改。即使在全大写标题中,也应该真正写成:“HE RAN 42 km IN 4 h”... - PhiLho
3
人们常常对Unicode大小写处理感到困惑,因为他们认为所有情况都像26个ASCII字母一样,但实际上并非如此。例如,考虑希腊字母的三个小写形式的情况。另外,还有一些小写编码点在映射时不会改变大小写。从某种意义上说,有四种Unicode大小写处理方式,其中“折叠大小写(fold case)”是第四种。要进行大小写不敏感的字符串比较,必须将每个字符串映射到它们的大小写折叠形式,并比较该映射的结果。 - tchrist
1
实际上,这并不是关于Unicode的问题,而是有关文化习惯的。德国人将ß大写为SS,Unicode只是尊重这种做法。 - Mihai Nita
@maaartinus 无论使用多少 uclc 的组合,都不能可靠地获得 Unicode 提供的折叠大小写映射。这就是为什么 Perl 提供了一个 fc 函数。如果你在 Java 中遇到困难,可以尝试使用 ICU 库,它可能会有一些解决方案。 - tchrist
@tchrist:这太疯狂了。它激发了我一个问题(https://dev59.com/w2Ik5IYBdhLWcg3wl_Ep)。 - maaartinus
显示剩余4条评论

3

特别适用于表意文字... :-) - PhiLho
1
在Java中,实际上无法正确处理Unicode的标题大小写。只有一个Character方法,没有像大写和小写那样的String方法。这是一个真正的问题。 - tchrist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接