Java:如何将双码点ä(U+0061+U+0308)转换为单码点ä(U+00E4)?
Unicode组合字符转合成字符的解决方案
先搞懂你遇到的这个ä是什么
你说的这个由U+0061(小写a)+U+0308(组合变音符号¨)组成的ä,属于Unicode分解形式(NFD,Normalization Form D)——是把带变音的字符拆成基础字符+组合标记的形式;而文件系统里的U+00E4是合成形式(NFC,Normalization Form C),是把基础字符和组合标记合并成单个预定义的字符。
这和代理对完全没关系:代理对是用来表示Unicode中超出BMP(基本多语言平面,U+0000到U+FFFF)的字符,用两个16位编码单元组合,你的这两个码点都在BMP范围内,所以不属于代理对。
你粘贴时显示成单字符是因为系统渲染引擎会自动把组合序列合并显示,但底层存储还是两个独立码点,所以UTF-8编码后十六进制是61 cc 88(U+0061对应61,U+0308对应cc 88)。
如何转换?
可以通过Unicode标准化(归一化)操作,把分解形式转成合成形式,主流编程语言都提供了对应API:
Java示例
使用java.text.Normalizer类,指定归一化形式为NFC:
String decomposed = "\u0061\u0308"; // 分解形式的ä String composed = Normalizer.normalize(decomposed, Normalizer.Form.NFC); // 此时composed就是"\u00E4",对应UTF-8十六进制为"C3 A4"
Python示例
使用unicodedata.normalize方法:
import unicodedata decomposed = '\u0061\u0308' composed = unicodedata.normalize('NFC', decomposed) # composed == '\u00E4'
你之前调用getBytes无效的原因是:getBytes只是把字符串按指定编码转成字节数组,不会处理Unicode字符的归一化——归一化是字符层面的逻辑操作,和编码转换是完全独立的两个步骤。
内容的提问来源于stack exchange,提问作者machekj
相关产品推荐
相关产品推荐

