许多人用 Java 处理到中文资料时常会出现乱码关于 Java 和中文兼容性的问题实在让许多程序员为此伤透脑筋相关的问题每隔几天就会出现在网络上为了舒缓您紧蹙的眉头我特别写了这系列文章解说 Java 牵涉到文字时的内部处理方式供读者参考读完本系列文章之后不求甚解者可以治标充分理解者可以治本本文贵在原理解说别光是囫囵吞枣
快速解决之道
如果你目前正遭遇到 Java 和中文不兼容的问题请你注意下面这几点说不定问题能马上迎刃而解
检查操作系统设定先检查你的操作系统确定国籍语言资料是「Traditional Chinese(Taiwan)」国籍语言资料的设定会影响 Java 编译器与JRE的判断我之前就是因为国籍资料设定不正确出了一堆 Java 和中文不兼容的怪事
更新 Java 环境版本改用最新版的 JDK新版本的 JDK 说不定已经解决你原有的问题请注意某些 Java IDE 所用的编译器和 JRE 是不兼容于中文的(我遇过这样的情形)你最好能把 Java IDE 的 JDK 指到新版的 JDK另外如果数据库取回的资料是乱码换别套或者更新 JDBC 驱动程序试试看
如果还是无法解决请详细阅读下面各小节的内容仔细推敲你的错误所在
UnicodeUTFUTF
Java 内部处理字符使用的字序方式是 Unicode这是一种通行全球的编码方式Unicode 因为必须将中韩日英法阿拉伯……等许多国家所使用的文字都纳入目前已经包含了六万多个字符所以 Unicode 使用了 个位来为字符编码因为 Unicode 使用了 位编码所以每个字符都用 位来储存或传输是很自然的事这种储存或传输的格式称为 UTF(是不是很像战斗机的名字)如果你使用到的字符都是西方字符那么你一定不会想用 UTF 的格式因为体积比 位的 Latin(一种扩充 ASCII 的编码)多了一倍所以 Unicode 另有一种储存或传输的格式叫做 UTFUTF 的格式在编码英文时只需要 位但是中文则是 位所以中文字出现比例高的地方还是使用 UTF 比较节省空间Java 的 Class File(也就是 bytecode)中有一字段叫做常数区(Constant Pool)一律使用 UTF 为字符编码
关于 Unicode 的编码请查阅「The Unicode Standard Version 」一书(AddisonWesley 出版)关于 UTF 编码请查阅「Java I/O」一书的 页(OReilly 出版)关于 Java Class File 的格式与 Constant Pool请查阅「Java Virtual Machine」一书(OReilly出版)
Unicode 与繁体中文编码的互转
虽然 Java 内部完整地使用 Unicode但是你所使用的操作系统可不见得以繁体中文版的 Windows 来说预设的编码方式是 MS这是一种兼容于 Big 的编码方式字符串数据从 Windows 一送进 JREJRE 的转码系统马上先把字符串编码由 MS 转成 Unicode才能进行处理字符串资料由 JRE 一送出给 WindowsJRE 的转码系统马上先将其由 Unicode 转成 MS操作系统才能处理
想知道你的 JDK 或 JRE 会用什么样的编码方式来和操作系统沟通请执行下面的 Java 程序
public class ShowNativeEncoding {
public static void main(String[] args) {
String enc = SystemgetProperty(fileencoding);
Systemoutprintln(enc);
}
}
如果执行结果不是下面的字符串之一那么你的操作系统国籍语言设定可能就有问题了
· Big这是繁体中文 de facto 标准
· CNS台湾的官方标准繁体中文编码
· Cp繁体中文加上 个使用者自定的字符
· Cp繁体中文版 IBM OS/ 用的编码方式
· Cp繁体中文版 IBM AIX 用的编码方式
· EUC_TW台湾的加强版 Unicode
· ISOCN编码中文的一套标准
· ISOCN_CNS编码中文的一套标准繁体版袭自 CNS
· MS 或 CpASCII + Big用于台湾和香港的繁体中文 MS Windows操作系统
· Unicode有次序记号的 Unicode次序记号占用两个 byte如果其值是xFEFF表示使用 bigendian(由大到小)的次序为 Unicode 编码如果其值是 xFFFF表示使用 littleendian(由小到大)的次序为 Unicode 编码
· UnicodeBig使用 bigendian(由大到小)的次序为 Unicode 编码
· UnicodeLittle使用 littleendian(由小到大)的次序为 Unicode 编码
· UTF使用 UTF 为 Unicode 编码
关于 Big 编码请查阅「CJKV Information Processing」一书的附录 H(OReilly出版)
编译时的注意事项
编译的时候如果你不说明原始文件编码方式的话 javac 编译器在读进此原始程序文件开始编译之前会先去询问操作系统档案预设的编码方式为何以繁体中文 Windows 来说javac 会先询问 Windows 得知档案是用 MS 的方式编码然后就可以将档案由 MS 转成 Unicode 编码方式开始进行编译
通常在编译阶段会造成的错误有下列几种可能
如果操作系统的国籍资料设定错误会造成 javac 编译器取得的编码信息是错的
较差劲的编译器可能没有主动询问操作系统的编码方式而是采用编译器预设的编码方式
如果原始程序不是用编译当时操作系统预设的编码方式存盘的也会造成错误比方说原始程序文件是台湾程序员写的在繁体中文版的 Windows上以 MS 编码存盘再经由网络传送到泰国在泰文版的 Windows 上编译(泰文版 Windows 预设的档案编码方式是 MS)
这种因为原始程序文件编码方式和编译器无法匹配所造成的问题轻则编译成功但执行时文字出现乱码或出现 Error/Exception重则无法成功编译这时候你需要主动透过「encoding」选项来指定原始程序的编码方式编译器会以你指定的编码为主不会再去询问操作系统下面的例子我们告诉编译器「TaiwanClassjava」是以繁体中文版 Windows 的「MS」编码的
javac –encoding MS TaiwanClassjava
如果你手上只有某 class 文件没有原始程序文件而且你确定其 constant pool 的UTF 字段编码错误你有两种方式可以用来修正编码
先反编译取得原始程序再修改编译
或者直接利用 bytecode 编辑软件直接修改 class 文件