1. JAVAC是以系统默认编码读入源文件,然后生成按UNICODE编码的CLASS文件。 摘自《Java GetBytes编码方式 http://nopainnogain.iteye.com/blog/970628 》
windows 系统默认编码一般是 GBK,Linux 可以通过 LANG 来设定.
可以通过指定编码方式改变Javac读入源文件的编码方式。
javac -encoding GBK Test.java
若一个源程序文件的编码是 GBK, 并且程序中涉及中文字符串,(手动输入的中文字符的编码方式就是当前文件的编码方式GBK. )
此时,若 LANG=UTF-8,使用 javac Test3.java 编译程序时会出现如下警告:
这是因为 javac 按照系统默认编码 UTF-8 来读取文件, 遇到里面的实为GBK中文字符无法在 UTF-8字符集中找到相应字符,所以出现警告。
而使用 javac -encoding GBK Test3.java 则不会出现警告
2. 程序运行过程中调用getBytes(),new String(),println 打印字符串显示的过程中,都有编码的转换过程.
Java 中的字符串都是按 unicode 方式存储的. 所有的编码转换均是从原始编码转化为 unicode,再由 unicode 转换为 目标编码的过程。
如下程序:
若 getBytes(),new String() 不指定相关编码, 则它会按照系统默认编码来处理
例如 gbkStr.getBytes() 会按照系统默认编码来返回字节流, new String(bytes)会认为 bytes 字节流是系统默认编码。
上述程序中的 gbkStr.getBytes().length 和 utfStr.getBytes().length 会在运行时由于 LANG 不同而出现不同的结果。
LANG = zh_CN.GB18030,终端编码为 GBK,运行结果为:
LANG = zh_CN.UTF-8,终端编码为 UTF-8,运行结果为:
从运行结果看,我们有个疑问,为什么第一个字符串打印出来显示都正常,而第二个字符串却显示不正常呢。
我们看看这行代码 new String(gbkStr.getBytes("GBK"), "UTF-8)
这句代码可能我们会理解为将 GBK 的 String 转化为 UTF-8 的String,这是大错特错的,实际上这行代码有严重错误,直接导致 utfStr 里存储的不是能正常解码的字符。
我们反复讲 JAVA中的String都是unicode编码的,所以不存在GBK编码的String”或“UTF8编码的String”这样的幼稚说法,上述程序的变量定义 gbkStr 和 utfStr 也是幼稚行为。
而这句话的实际含义是将gbkStr对象按照GBK的方式编码为byte[],然后再把 byte[] 按照 UTF-8 的方式存储到String中,(此句摘自《谈谈对Java中Unicode、编码的理解 http://blog.csdn.net/soleghost/article/details/959832 》).
这句话涉及的实际操作是:gbkStr(Unicode字符)转化为GBK字符,gbk字符转化为 byte[] (即转化结果存放到 bytes[] 中), 然后是 bytes[] 转化为 UTF-8 字符, UTF-8 字符转化为 unicode (即认为 byte[] 中是UTF-8字符的字节码,将byte[]转化为 Unicode).
将一个 gbk 编码的 byte[] 当作 utf8 方式存储很明显会出现错误,而正确代码是这样。由 gbkStr 对象得到 UTF-8 方式编码的 byte[], 在按照 UTF-8 方式存储到 utfStr.
LANG = zh_CN.GB18030,终端编码为 GBK,运行结果为:
LANG = zh_CN.UTF-8,终端编码为 UTF-8,运行结果为:
总结结果:
正确代码
不同环境下运行结果均是:28,36
不同环境下运行结果均是:28,44
注意,
这里由于源代码文件是 GBK 编码, 所以 是GBK编码,若文件是UTF-8编码,运行结果仍相同,结果与源代码文件中字符的编码毫无关系,这是因为 在赋值给 gbkStr 时已经完成了到 unidoce 的转化, gbkStr 中存储的是统一的 unicode (gbkStr.length()得到的实际上是字符串中unicode字符数量)。假若是从文件中读取字符串而不是这里这种直接语句赋值,则需要我们在读取文件时指定文件的编码格式才能得到正确的 String。而我们要得到某个编码的输出文件时,只需在将 String 输出到文件时指定编码格式就可以,非常方便。
因此:
要得到一个字符串按照 UTF-8 编码 和 GBK 编码的字节数组, 直接使用 string.getBytes(encoding) 就可以了,无需经过复杂的转化。
上述程序在不同的 LANG和终端环境下均打印显示正常,是因为在输出到终端时有一个 unidoce ==> 其它编码格式的转化。
由此可看出,java中将字符串使用 unidoce 统一编码十分方便(Python也是这样),而不像 C/C++ 中必须保证字符串的编码和LANG,终端环境一致才打印显示正常。
建议在使用 getBytes 和 new String 时均指定编码。
在上述代码中, gbkStr 和 utfStr 中存储的内容(unicode)是一样的, 它们都对应着 28 个字符, 一个英文或中文均只算一个字符. 我们可以查看一下内容:
Unicode 值
此外,我们也可以看一下 gbk 字节流 和 utf-8 字节流的内容。
GBK 字节流 (GBK中文字符按照两个字节编码,英文按照一个字节编码)
UTF-8 字节流(UTF-8中文字符编码方式是变长的,这里的几个中文字符是按3个字节编码)