该文章为本人于2018创作,可能有部分内容已过时
最近在做QEMU Guest Agent接口的时候,遇到一个中文乱码问题。
在读取网卡名字的时候,中文显示为锟斤拷are。
可能也是自己对于C语言不是很熟悉,没有做过什么拿得出手的东西。
现象
Linux 在调用的时候使用UTF-8读会乱码,用GBK读会出现锟斤拷。 Windows 端也显示乱码。
初步判断
Windows 端查询的内容在转换的时候转换失败或丢失了。
紧接着就去查询转换的地方,发现C语言有宽字节和多字节。
WideCharToMultiByte 函数调用。给指针分配内存空间的时候,少分配了一半的内存,改好之后,Windows一切正常。
此时满心欢喜的用Linux端调用尝试,本以为大功告成。
结果锟斤拷变成了双持的。。。锟斤拷太锟斤拷 (以太网)
有诗云:
手持两把锟斤拷,口中疾呼烫烫烫。
脚踏千朵屯屯屯,笑看万物锘锘锘。
排查
查询的时候查询到的是宽字节,也就是Unicode。但是在Linux终端编码UTF-8接收的是几个问号,GBK接收的是锟斤拷。
所以这里面一定有问题。
然后就想,能不能把查出来的GBK,转成UTF-8乱码传出来,Linux读取之后再转回GBK。
尝试一番,发现没读出来,可能是代码写的有问题。
于是就转换了思路,为什么要转换成GBK而不是直接从Unicode转换成UTF-8呢?
查阅了官方文档
发现,在WideCharToMultiByte 转换格式的时候可以指定Windows的ANSI和UTF-8。原来是之前的人写的代码有问题,没有考虑跨平台兼容性。
于是进行了转码,将Unicode直接转换为UTF-8。
再次使用Linux终端编码UTF-8接收,一切正常。
至此已经过去了一周的时间。
由于是上层平台需要使用此值,所以使用python代码接收,但是python2的代码中默认编码是ASCII,所以:
import sys
reload(sys) #这一句是因为python2.5以后不支持setdefaultencoding方法,需要重新载入模块才可以使用
sys.setdefaultencoding("utf-8")
但是使用了utf-8发现还是有问题。
发现向Java代码传递后值为\uXXXX类的值,这不是Unicode的编码吗。为什么不是中文?
于是看了一下接收的地方代码,接收的json数据,但使用eval去转换str类型变成dict。
在查阅了一番资料后,得知一个结论: 不要使用eval,eval会直接进行转换,而且之前听说eval是一个不安全的操作,所以尽量使用json包去转换类型。
改为json转换类型,发现结果还是不对,但是执行print去打印此值就是正确的值。
此时判断,从python到Java之间的转换层出了问题,于是一点点排查坑。
最终发现在一个不起眼的地方,在代码中间部分被引入了json包,而没有在头中引入。
前人挖坑,后人遭罪啊。。。
于是检查json转换逻辑,发现json.dumps 序列化时对中文默认使用的ascii编码。
想输出真正的中文需要指定ensure_ascii=False。
测试后一切正常。
又过去了一周的时间。。。。
两周的时间对于开发来讲时间挺长的,但是经历过这个坑,下一个坑就容易多了。
总结的经验
在解决问题的时候,尽量去溯源,不要在中间过程中下太多功夫,一般都是代码出口或者入口上出问题的几率比较大。
这次就是钻了牛角尖,妄想着在Linux和Windows通信的时候把编码转换正常,浪费了这么多时间。
呼吁大家编码的时候遵循编码规范。
建议参考的编码格式
Java:阿里巴巴Java开发手册
Python:PEP8规范/Google代码规范
C:Google代码规范