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

Bri*_*Hsu 1 java unicode scala 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?
Run Code Online (Sandbox Code Playgroud)

如您所见,它在第一种情况下正确转换,三个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个字节.

Rex*_*err 7

Java(以及Scala)对其字符串使用UTF-16编码,这意味着所有超过2 ^ 16-1的unicode代码点必须用两个字符表示.(实际上,编码方案比这复杂一点.)无论如何,这length是一种在较低级别操作的方法 - 字符 - 因此它返回字符数.

如果你想找出代码点的数量,当你说"两个unicode字符"(例如两个打印出来的符号)时,你可能会直觉地想到这个,你需要使用s.codePointCount(0,s.length).如果你想将它们转换为十六进制,你需要使用代码点而不是Chars,因为并非所有代码点都适合.我对这个问题的回答包含将字符串转换为代码点的Scala代码.(不是效率最高;如果你在大字符串上进行重载文本处理,你想要重写它以使用数组/ ArrayBuffer.)