在Java / Scala中将Unicode(CJK ExtB)字符转换为十进制NCR

时间:2011-03-07 09:32:31

标签: java scala unicode character-encoding unicode-string

我正在尝试将包含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似乎认为它是一个包含三个字符的字符串。

那么,这里发生了什么,我怎么能正确转换第二种情况,就像我提到的网站上的转换器一样?非常感谢。

更新

  • 我的源代码文件使用的是UTF-8。
  • 这是“怀”.toCharArray()的结果
    • char[] = ?, char.toInt = 55390
    • char[] = ?, char.toInt = 56743
    • char[] = 懷, char.toInt = 25079

现在我想我知道发生了什么。字符“”在UTF-16中编码为0xD85E 0xDDA7,它是4个字节而不是2个字节。因此,转换为char数组时需要2个元素,其中数据类型char只能表示2个字节。

3 个答案:

答案 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,请检查它是否合适。