是一个极度小众但极度阴间的BSTR编码相关问题。
这文章也只是做记录罢了。
因为遇不到问题的自然遇不到(毕竟这阴间问题触发条件还挺刁钻的,也因此网络基本没有这个问题的讨论)
遇到的也查不到这篇文章
就算查到了也没有解决办法(除非不借助msado15.dll访问数据库)
缘起:
我在做课设。
我要存储若干个中文项,而数据库不能存储数组。
因此考虑用JSON库转成JSON,再上传数据库存储
然后JSON的官方编码是UTF-8,因此网上找到的能用的库,基本上是利用UTF-8的JSON库
问题在于windows中国语言的默认编码是GBK,从用户使用的角度讲,我也不可能让用户为了使用我这个垃圾软件而更改系统控制台的全局编码为UTF-8。
因此我花了亿点时间找到了GBK转UTF-8编码的办法。本来以为万事大吉,妹想到……
问题:
把这段JSON处理过的UTF-8编码的数据上传到数据库,再从数据库取回来……
发现UTF-8字符的编码被改变了!!!!!!!!!
发现UTF-8字符的编码被改变了!!!!!!!!!
发现UTF-8字符的编码被改变了!!!!!!!!!
wtf?
然后花了一整天的时间排查。
首先怀疑是JSON库拉跨了,后来发现不妥
然后怀疑是SQL Server存储拉跨了,后来发现也不对
最后发现
是TM的,C++的数据库访问库(msado15.dll)有bug……
首先看一眼出错的语句:
排除SQL,排除JSON,剩下的是:
先吐槽:这也太阴间了吧,这真的属于是历史遗留问题了。而且这种问题,往往是 不知道的时候十分折磨,知道了之后也很难分享出去,就导致每个遇到这种阴间问题的人,基本上都得从头自己排错,像我这样。
查错
来看我的查错步骤
首先转换成utf8
这一步dump,是把它变成["…","…"]的JSON形式,是UTF8的编码
所以vs的智能提示(默认GBK解码)显示字符串无效
将它输出到控制台是这样的,这是强行用GBK去解析UTF8编码的字符串
和我在SQL看到的GBK一样
但是注意嗷,它只是输出乱码,存储的UTF8编码仍然是正确的 43个字节
这一步,直接用const char*(c_str()返回的)类型的字符串,构造一个string出来,发现仍然正常
这一步,在char*的基础上,先转换成了_bstr_t,再转换成string
发现出大问题了 1. 的智能提示可以正常侦察了,2. size变成41了
这意味着字符的编码已经被改变了!!!
但是这不一定就是_bstr_t的问题,也许只是_bstr_t和string配合的不是很好?
还有洗的余地是吧
洗,都可以洗 于是我加了一组
我直接用char的数组去拷贝这个东西
发现结果是同样地被改变了
_bstr_t没得洗
就是它 改变了utf的编码
而且还有一个坏消息
数据库提取的变量转字符串,必须要经过_bstr_t ,除非换库
我泪目了
我真没想到是_bstr_t出问题
为什么它要这么做
而且它到底做了什么
难道是把翻译不了的字符全部替换为问号了吗
(O(∩_∩)O是的呢!!~)
注意到这里的?是个编号为63的
查表得?的ASCII码为63
***的_bstr_t真的把无效字符替换为?了
我草
搞几把啊
为什么要这么做??
懂了_bstr_t的魔力就是把翻译不出的字符全部替换为ASCII为63的问号
这可真是太贴心了
谁设计的东西
估计熬不过今晚了
哪个nt程序员干的事
居然还是C++官方是吧
那没事了
就这样吧
麻了
补救的办法:
GBK转UTF做相关处理,处理完后转回GBK上传数据库
由于机器编码环境是GBK,因此_bstr_t读取GBK编码不会出现上述问题
虽然两次转换有点脱裤子放屁的意味就是了
总之不能让_bstr_t读取无效字符
猜测:如果修改了机器(或者项目?)的编码环境,估计就不会出现上述问题了,可以试试。