本文建议阅读时长:15mins
前记:最近笔者工作比较繁忙,导致本来应该有很多博客需要填坑,一直也没有静下心来好好整理下,今天蹭着夜深人静加之阵阵柔和的轻音乐正好先写上一篇,也算是为自己乱码专栏开个头,当然本专栏主要解决Tomcat服务器以及一些国产中间件在应用使用过程中的乱码情况的总结和原理解释,该篇作为乱码专栏的第一篇。
回归正题:先说一下今天的出错场景,业务在前端传递String对象到后台,后台在将获取String对象将其转化为byte数组,存入数据库的一个过程。在存入数据库时,我们查看String对象是未乱码的,当我们在oracle数据库中读取查看的时候出现乱码。那么对于这个问题,是否有一个大致的解决思路了呢?
解决方案:修改jvm属性-Dfile.encoding的编码等于GBK解决。原因简单描述为数据库的编码为gbk,而getBytes获取到byte数组的编码为utf-8,所以在查询过程中出现乱码。当然也可以通过修改oracle数据库的编码为UTF-8解决该问题,只要传入和读取的过程编码保持一致即可。具体分析原因可见下文。
代码如图1所示,问题也在上面的正题中提到了,那么很明显笔者一开始想的是该jsonStr对象是不是乱码,在测试过程中发现是正常,那么该乱码过程就可能在存库过程和获取字节数组过程中出现乱码,于是乎笔者查了下当前数据库的编码,通过执行该sql语句,查询得知当前数据库的编码为GBK,查询sql如图2。
结合上面的过程,那么问题清晰可见,我们只需要查明jsonStr.getBytes()的编码过程是否是以GBK编码即可,我们先写一个简单的demo如图3测试一下。基于JDK1.8.0_171测试。
从上面的图4可以看出,file.encoding属性可以修改当前的getbytes()的编码。查看下面的代码块JDKString.getBytes()源码可知,获取编码的方式是可以通过file.encoding属性控制该值,因此对于上述的问题在后续我们也查出了乱码问题是由于数据库的编码和getbytes()默认的编码不一致导致的乱码,因此我们在知道原理的基础上修改服务器默认的file.encoding的编码,使其与数据库保持一致,即解决当前问题,也彻底搞清了整个过程。当然也可以修改数据库的编码,这样其实是同样的道理,只需要保持输入和输出编码一致,即不会产生乱码问题。
java.lang.String.class
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);//调用StringCoding对当前值进行编
//码操作,并返回编码后的byte数组。
}
java.lang.StringCoding.class
static byte[] encode(char[] ca, int off, int len) {
String csn = Charset.defaultCharset().name(); //获取默认的charset编码。
try {
// use charset name encode() variant which provides caching.
return encode(csn, ca, off, len); //根据上面获取的编码对该对象进行编码操作
} catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn);
}//下面方法代码忽略,有兴趣自己查看jdk源码
}
java.nio.charset.Charset.class
public static Charset defaultCharset() {
if (defaultCharset == null) { //如果当前编码为空,则读取jvm中的file.encoding的编码。
synchronized (Charset.class) {
String csn = AccessController.doPrivileged(
new GetPropertyAction("file.encoding"));
Charset cs = lookup(csn); //查找当前file.encoding定义的编码。
if (cs != null)
defaultCharset = cs; //查找到即返回当前编码,负责则默认返回UTF-8编码
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
常用几种编码介绍:ASCII、ISO8859-X、GBK、UTF-8等。
编码介绍转载自:常见的几种编码方式_科氏加速度的博客-CSDN博客_常见的几种编码方式
ASCII:这是美国在19世纪60年代的时候为了建立英文字符和二进制的关系时制定的编码规范,它能表示128个字符,其中包括英文字符、阿拉伯数字、西文字符以及32个控制字符。它用一个字节来表示具体的字符,但它只用后7位来表示字符(2^7=128),最前面的一位统一规定为0。
GBK:GBK和GB2312都是针对简体字的编码,只是GB2312只支持六千多个汉字的编码,而GBK支持1万多个汉字编码。而GB18030是用于繁体字的编码。汉字存储时都使用两个字节来储存。
UTF-8:是Unicode字符的实现方式之一,它使用1-4个字符表示一个符号,根据不同的符号而变化字节长度。互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
专栏下文预览:笔者对于乱码的问题结合公司同事和自己工作所得大概总结出乱码在请求/响应过程中主要有4个地方可能出现乱码,如下图5所示:对于每个过程中编码解码过程的乱码解决方案不尽相同,这个将在该专栏的后续文章中具体阐述分析。
与君共勉: 编程就是个不断学习的工作,在结合源码和思考中寻求每一个问题的本源,这样我们才会在这条路上越走越远。