bytes数组转string指定编码_强烈建议使用UTF编码

ccd50d7df3eff09ee5c666d9a1cf039f.gif

32f53079fe614ea445c75ced18b97283.gif

496d27a735ee72e9ef5bda448e0bb727.png

Java的乱码问题由来已久,有点经验的开发人员肯定都遇到过乱码问题,有时是从Web上接收的乱码,有时是从数据库中读取的乱码,有时是在外部接口中接收到的乱码文件,这些都让我们困惑不已,甚至是痛苦不堪,看如下代码:

package com.company;

import java.util.Scanner;

public class Client {
  public static void main(String[] args) {
    //接收键盘输入
    Scanner input = new Scanner(System.in);
    while(input.hasNext()){
      String str = input.nextLine();
      //使用split方法分隔后统计
      int wordsCount = str.split(" ").length;
      System.out.println(str + " 单词数:" + wordsCount);
    }
  }
}

Java文件是通过IDE工具默认创建的,编码格式是GBK,大家想想看上面的输出结果会是什么?可能是乱码吧?两个编码格式不相同。我们暂不公布结果,先解释一下Java中的编码规则。Java程序涉及的编码包括两部分:

(1)Java文件编码如果我们使用记事本创建一个.java后缀的文件,则文件的编码格式就是操作系统默认的格式。如果是使用IDE工具创建的,如Eclipse,则依赖于IDE的设置,Eclipse默认是操作系统编码(Windows一般为GBK)。

(2)Class文件编码通过javac命令生成的后缀名为.class的文件是UTF-8编码的UNICODE文件,这在任何操作系统上都是一样的,只要是class文件就会是UNICODE格式。需要说明的是,UTF是UNICODE的存储和传输格式,它是为了解决UNICODE的高位占用冗余空间而产生的,使用UTF编码就标志着字符集使用的是UNICODE。

再回到我们的例子上,getBytes方法会根据指定的字符集提取出字节数组(这里按照UNICODE格式来提取),然后程序又通过newString(byte[] bytes)重新生成一个字符串。来看看String这个构造函数:通过操作系统默认的字符集解码指定的byte数组,构造一个新的String。结果已经很清楚了,如果操作系统是UTF-8编码的话,输出就是正确的,如果不是,则会是乱码。由于这里使用的是默认编码GBK,那么输出的结果也就是乱码了。我们再详细分解一下运行步骤:

步骤1 创建Client.java文件。

该文件的默认编码GBK(如果使用Eclipse,则可以在属性查看到)。

步骤2 编写代码(如上)。

步骤3 保存,并使用javac编译。

注意我们没有使用“javac-encoding GBK Client.java”显式声明Java的编码格式,javac会自动按照操作系统的编码(GBK)读取Client.java文件,然后将其编译成.class文件。

步骤4 生成.class文件。

编译结束,生成.class文件,并保存到硬盘上。此时.class文件使用的是UTF-8格式编码的UNICODE字符集,可以通过javap命令阅读class文件。其中“汉字”变量也已经由GBK编码转变成UNICODE格式了。

步骤5 运行main方法,提取“汉字”的字节数组。

“汉字”原本是按照UTF-8格式保存的,要再提取出来当然没有任何问题了。

步骤6 重组字符串。

读取操作系统的编码格式(GBK),然后重新编码变量b的所有字节。问题就在这里产生了:因为UNICODE的存储格式是两个字节表示一个字符(注意这里是指UCS-2标准),虽然GBK也是2个字节表示一个字符,但两者之间没有影射关系,要想做转换只能读取映射表,不能实现自动转换—于是JVM就按照默认的编码格式(GBK)读取了UNICODE的两个字节。

步骤7 输出乱码,程序运行结束。

问题清楚了,解决方案也随之产生,方案有两个。

步骤8 修改代码。明确指定编码即可,代码如下:

步骤9 修改操作系统的编码方式。


各个操作系统的修改方式不同,不再赘述。我们可以把从字符串读取字节的过程看作是数据传输的需要(比如网络、存储),而重组字符串则是业务逻辑的需求,这样就可使乱码现场重现:通过JDBC读取的字节数组是GBK的,而业务逻辑编码时采用的是UTF-8,于是乱码产生了。

对于此类问题,最好的解决办法就是使用统一的编码格式,要么都用GBK;要么都用UTF-8,各个组件、接口、逻辑层都用UTF-8,拒绝独树一帜的情况。

问题解释清楚了,我们再来看以下代码:

package com.company.section2;


public class Client {
  public static void main(String[] args) throws Exception {
    String str = "汉字";
    //读取字节
    byte[] b = str.getBytes("GB18030");
    //重新生成一个新的字符串
    System.out.println(new String(b));
  }
}

仅仅修改了读取字节的编码格式(修改成了GB2312格式的),结果会是怎样的呢?又或者将其修改成GB18030,结果又是怎样的呢?结果都是“汉字”,不是乱码。哈哈,这是因为GB2312是中文字符集的V1.0版,GBK是V2.0版本,GB18030是V3.0版,版本是向下兼容的,只是它们包含的汉字数量不同而已,注意,UNICODE可不在这个序列之内的。

本文参考自:《编写高质量代码:改善java程序的151个建议》

作者:秦小波

声明:本文只供学习使用,未涉及任何商业利益,如有侵权,立删。

支持作者

赞赏就不用啦,生活都不易,右下角的“在看/赞”点一下,如果感觉文章不错,记得分享到朋友圈让更多人知道!

05ed92c7c5f79fbc318b37e4d5eeca51.png

2b668a5747591dc3eac6674021f19476.gif

点分享

531b5fbc1571b44be11ec4e73321d4d0.gif

点点赞

b7c85a44ec83c1356f75e336f45f8892.gif

点在看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值