当前位置:   article > 正文

qt中文乱码原因分析及解决方案_qstring strtemp = qstring::fromutf8("中文"); 显示乱码

qstring strtemp = qstring::fromutf8("中文"); 显示乱码

首先说明几个基础概念

  • UTF-8 BOM 和 UTF-8。BOM在文件头位置占三个字节,用来标识UTF-8编码,软件通过BOM来识别这个文件是否是UTF-8编码。
  • 源码字符集。源码文件使用某种编码格式保存。
  • 执行字符集。可执行程序内保存的编码(程序执行时内存中字符串编码)。
  • QString中统一采用utf-16(Unicode)存储字符串。
  • QString::fromxxx()函数。代表将字符串以对应格式去解析成Unicode,fromLocal8Bit()代表使用本地字符集(系统设置)转换成Unicode;fromUtf8代表从utf-8转换成Unicode。Qt程序运行的时候字符串编码在QString中实际都是以unicode编码存储的,通过使用QString::fromxxx()转换成转换成unicode存到QString中。

乱码产生的原因

中文处理的时候,编译器首先解析源码字符集,根据具体情况将源码字符集解析为执行字符集,再将执行字符集转换为QString的编码(unicode)。QString的编码是固定的,要想整个过程无乱码,那么源码字符集到执行字符集的转换必须确定。换句话说就是编译器必须明确文件的编码和执行字符集。这个不同的编译器的逻辑是不一样的。以MSVC和Mingw为例分析:

  • MSVC 

源码字符集:源文件有BOM的情况下按BOM解释,无则使用本地Locale字符集(随系统设置而变)。

执行字符集:使用本地Locale字符集(随系统设置而变)。

  • Mingw

源码字符集和执行字符集默认都按utf-8解析。

因此乱码的原因有:

  1. 编译器解读源码字符集错误,比如utf-8的源码不带bom编译器会当成locale,执行字符集也是locale所以不需要转换,但是实际utf-8的编码到locale是需要转换的。
  2. 源码字符集到执行字符集的转换错误。比如需要解析执行字符集utf-8,需要使用fromUtf8。但是却使用了其它的转换方式fromXXX,这是使用了错误的转换算法。
  3. 字符解析错误。比如程序中的字符串二进制是utf-8的,设置执行字符集是loacle,那么解析就会出错。

解决方案

最好的解决方案是将源码保存成utf8 bom,执行字符集也选为utf8

对于MSVC,源码字符集之所以保存成 bom是因为只有bom 编译器才能识别出来源码字符集,执行字符集设置为utf8在C++98的MSVC上是没有什么好办法的,只能使用QString::fromUtf8设置转换算法。c++ 11比较方便,提供了设置执行字符集的方法 #pragma execution_character_set("utf-8")。

之所以说这是最好的解决方案,是因为这种设置在qt5以上的版本中具有良好的跨平台性,Mingw和GCC都支持这种方案。

采用这种方案无需再写QString::fromxxx()函数(此中情况下发现无论是在msvc还是mingw下,对于中文常量的处理加不加fromUtf8都能正常处理,分析了一部分源码和注释后猜测是因为从const char*到QString的隐式转换内部使用了QStringData保存字符串的空间,然后这个字符串 从 utf-8转码为unicode并拷贝到data)。

使用vs2010以上版本开发程序的时候,可以安装ForceUTF8(with BOM)插件,能够使新创建的工程文件都是BOM格式。

#pragma execution_character_set("utf-8")不需要硬编码到代码中,可以使用inc文件在pri文件中设置编译器和链接器flag参数即可

  1. 将#pragma execution_character_set("utf-8")保存在xxx.inc中
  2. 在pri中添加:

win32-msvc*:QMAKE_CXXFLAGS += -FI\"$$PWD/xxx.inc\"

win32-msvc*:QMAKE_CFLAGS += -FI\"$$PWD/xxx.inc\"

另外有一点需要注意,编译器根据ui文件生成的ui_xxx.h文件默认编码是utf-8(无BOM),里面涉及到的汉字都已经转换成了字符编码,所以ui文件生成的源码无需特殊处理。

 

     

应该避免的解决方案

  • qt5之前有一种很不推荐的解决方案:

QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));  

      QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));  

      QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));

Qt5版本取消了QTextCodec::setCodecForTr()和QTextCodec::setCodecForCStrings(方法,只剩下setCodecForLocale,这一个函数也只影响Qt对toLocal8Bit相关函数的编码方式。并且QObject::tr是用于程序国际化的,可以使界面文字翻译成不同的语言。如果使用QObject::tr,就应该全部用英文表示,然后借助Linguist翻译成中文,这样不会乱码了。

  • 还有一种说法说用QStringLiteral也可以处理汉字乱码,其实它的功能是用来减少一次临时拷贝对象,用来提升性能的,有部分编码转换的功能,但是说它可以处理乱码,经过分析和实践发现毫无道理。

QStringLiteral是一个宏,在支持lambda表达式的c++11中,直接解析源码字符集到宽字节。不支持lambda的编译环境中等同于fromUtf8

 

只要保证源码字符集编译器识别正确就不会出现乱码,因为它是直接转换成了宽字符,和执行字符集没有关系(个人认为)

image.png

这种情况下,设置源码字符集为utf-8 bom,编译器因为bom的存在能够正确识别出源码字符集。但是执行字符集并没有设置,所以采用window系统默认的gbk作为执行字符集。因为QStringLiteral不经过执行字符集的转换,所以显示正常。

image.png

 

另外一种情况,源码字符集为utf-8,因为没有bom,msvc不知道这是什么编码,默认当成window系统默认的gbk作为源码字符集。但是真实编码应该是utf-8,源码字符集错了,转换成宽字节的时候也不正常了。

image.png

image.png

  

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/300309
推荐阅读
相关标签
  

闽ICP备14008679号