Java编码理解

Java编码理解

关键字:Java编码,Unicode, getBytes()
来源于我的笔记: 有道笔记-Java编码理解

各类程序涉及的编码不外乎这些: 源代码文件的编码, 程序中字符串的编码, 程序执行输出显示时的编码.

Java 号称国际化的语言,是因为它的 class 文件采用 UTF-8 ,而 JVM 运行时使用 UTF-16。
摘自《谈谈对Java中Unicode、编码的理解   http://blog.csdn.net/soleghost/article/details/959832 》

在java程序的编译和运行过程中都涉及了编码转换.

 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个字节编码)


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值