我正在尝试将包含CJK ExtB计划中的Unicode字符的Java字符串转换为十进制NCR。
例如(您可以使用http://people.w3.org/rishida/tools/conversion/)尝试:
游鍚堃
𧦧懷
这是我尝试过的(在Scala中):
def charToHex(char: Char) = "&#%d;" format(char.toInt)
def stringToHex (string: String) = string.flatMap(charToHex)
println (stringToHex("游鍚堃")) // 游鍚堃
println (stringToHex("懷")) // ��懷
println ("懷".toCharArray().length) // Why it is 3?
如您所见,它在第一种情况下正确转换,三个unicode字符转换为三个NCR。
但在第二种情况“怀”中,只有两个unicode字符,但Java / Scala似乎认为它是一个包含三个字符的字符串。
那么,这里发生了什么,我怎么能正确转换第二种情况,就像我提到的网站上的转换器一样?非常感谢。
更新
char[] = ?, char.toInt = 55390
char[] = ?, char.toInt = 56743
char[] = 懷, char.toInt = 25079
现在我想我知道发生了什么。字符“”在UTF-16中编码为0xD85E 0xDDA7,它是4个字节而不是2个字节。因此,转换为char数组时需要2个元素,其中数据类型char
只能表示2个字节。
答案 0 :(得分:7)
Java(以及Scala)对其字符串使用UTF-16编码,这意味着所有超过2 ^ 16-1的unicode代码点必须用两个字符表示。 (实际上,编码方案是bit more complex than that。)无论如何,length
是一种在较低级别操作的方法 - 字符 - 因此它返回字符数。
如果你想找出代码点的数量,当你说“两个unicode字符”(例如两个打印出的符号)时,你可能会直觉地想到这一点,你需要使用s.codePointCount(0,s.length)
。如果你想将它们转换为十六进制,你需要使用代码点而不是Char
s,因为并非所有代码点都适合。我对this question的回答包含将字符串转换为代码点的Scala代码。 (没有最大效率;如果你在大字符串上进行重载文本处理,你想要重写它以使用数组/ ArrayBuffer。)
答案 1 :(得分:2)
这是他们在unicode中所谓的“代理人”。例如,
"懷" foreach { c =>
println(java.lang.Character.UnicodeBlock.of(c))
}
打印
HIGH_SURROGATES
LOW_SURROGATES
CJK_UNIFIED_IDEOGRAPHS
顺便说一句,我也在台湾。如果您对Scala感兴趣,我们应该聚在一起聊聊。如果您有兴趣,我的电子邮件会在我的个人资料中。
答案 2 :(得分:0)
检查文件编码。您的IDE或构建脚本必须知道该文件是UTF-8或UTF-16(您使用哪一个?)。如果您定义BOM,请检查它是否合适。