前言
对于中文乱码的问题,我一向是比较害怕的,因为,,,一直都没搞明白过,但是,最近在调试公司一个项目,遇到了一个“奇怪”的中文乱码的 问题,花了一个晚上,终于搞明白了。为了避免以后再次踩坑,也希望能帮到遇到同样困境的童鞋,所以在此简单记录一下。
问题描述
问题的背景大概是这样,这是一个JAVA WEB项目,有一个需求是提供一种文本的模板,用户可以编辑,保存。考虑到文本内容特别长,我们数据库中使用BLOB
类型来存储文本数据,它对应的JAVA的byte[]
。一次,我在本地(Windows)启动服务调试代码,然后浏览器编辑上传了一个模板,我预览的时候,居然中文乱码了,乱码了!!!要知道,这个功能之前在服务器一直是正常运行,没有发生过乱码的问题。
解决过程
怎么办?其实我可以不管这个问题的,反正服务器是好的嘛!但是,以我的性格,这种问题一定要搞清楚我心里才安心。
首先,我想到的是,检查数据库原始的文本是否乱码。我通过Navicat工具去数据库里复制出来那段文本,能正常显示中文,只是在浏览器预览的时候乱码了。
看来,是程序读的时候出的问题。我在检查读取这段文本代码的时候发现,后台做了一个本地文件缓存,先将模板先写入本地文件,然后直接读取文件内容,下面是缓存文件。
通过上面代码可以发现,这里原封不动的把数据库读出来的字节码写入到了文件。就说明,数据库的数据和文件的数据是完全相同的,这个时候,我习惯性的检查了缓存文件的编码格式,WTF,居然是ANSI
。
这就让我很不解了,明明DB里面的默认编码格式是UTF-8
,为什么写入文件就变成了ANSI
呢?
这里值得注意的一点是,明明写入文件的时候用的是字节流,按道理来说跟字符集没有关系的,只有使用字符流的时候才会涉及字符编码问题。 按照这个逻辑的话,除非存入数据库里面的字节码有问题,但是我从数据库复制出来查看明明是没有问题的啊,为什么会有这样的矛盾呢?
带着这个疑问,我决定跟踪一下存入数据库的步骤。
首先,我确认了从浏览器HTTP传到后台接收的字符串变量vm
中是没有问题的。
然后,我发现了这样一段代码
原来,从String
到byte[]
的转化过程,使用了
vm.getBytes();
跟踪进去查看源码发现,调用了系统默认的字符集来实现从string
到byte[]
的编码。
而我Windows默认正好是ANSI
,也就是GBK
的编码。乱码原因很有可能就是因为encode的时候使用的是GBK
而decode的时候使用的是UTF-8
(decode的时候是根据数据库指定的编码,经过证实,就是UTF-8),两种编码不匹配。
为了验证这个猜想,我更改tomcat了的一个参数
重新启动tomcat,再次尝试提交模板,果然,这时候默认的字符集编码变成了UTF-8
,再次预览的时候,乱码消失,搞定!
解决方案
其实上面的方法只是我用来验证这个猜想的,至于解决方案,我还是采取了保险一点办法,在String
转化成byte[]
的时候,指定UTF-8
,毕竟,谁能保证服务器上的默认字符集一定是UTF-8
呢?
vm.getBytes("UTF-8");
===========分割线================
好气哦,这么一个小BUG折腾我半天,其实,这还没有结束。不知道胖友们注意到一个坑没有。在之前我一直相信数据库的数据没有问题,你看,数据库默认utf-8字符集,vm的内容复制出来看也没有乱码,可是,这到底是哪里不对呢?
其实,仔细一想,这个问题还真的不难解释。
- 首先,
blob
字段是存的字节码,这根本不关乎数据库的字符集; - 其次,之所以我的Navicat读取的vm数据没有乱码,这其实是一个错误的巧合。存入数据库的时候使用的是我本机的默认字符集编码,而解析的时候,Navicat把读取到的字节码转化成人能够理解的字符,也使用的我本机默认的字符集,这样使用同样字符集encode和decode正好没有出现乱码。
- 上面这条解释其实也印证了为什么之前模板的数据用Navicat查看是有乱码的。
===========分割线================
PS:以上,有什么的问题,欢迎提问,或者有什么错误的地方,也欢迎赐教 !