彻底弄懂Qt的编码(汉字乱码问题及相关函数作用)

彻底弄懂Qt的编码(汉字乱码问题及相关函数作用)测试1新建test工程用于测试,main.c文件内容如下:#include<QCoreApplication>#include<QDebug>intmain(intargc,char*argv[]){QCoreApplicationa(argc,argv);QStringstr_hanzi("百度");//汉字QStringstr_ascii(

大家好,又见面了,我是你们的朋友全栈君。

测试1

新建test工程用于测试,main.c文件内容如下:

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str_hanzi("百度"); // 汉字
    QString str_ascii("baidu.com"); // 字母

    qDebug() << str_hanzi;
    qDebug() << str_ascii;

    return a.exec();
}

运行, 输出结果如下:
这里写图片描述

不出意料的乱码。

下面开始分析。

此时,源代码main.c的编码是UTF-8 BOM。
这里写图片描述

使用Notepad++查看,编码也是UTF-8 BOM。说明的确是UTF-8编码。

查看目标文件main.obj,目标文件路径如下:
这里写图片描述

使用Notepad++打开,搜索”百度”二字,结果如下:
这里写图片描述
同时可以看到此时Notepad++显示文件内容使用的编码是ANSI,即本地编码,可以正确的显示汉字。那就说明,编译生成*.obj目标文件时,编译器将源码中的字符串由UTF-8 BOM转换成了本地编码。

接下来查看test.exe的内容,同样搜索”百度”二字,如下图:
这里写图片描述
和理论一样,因为.exe文件就是.obj文件链接起来生成的。

到此为止,我们可以看到,源码中的汉字字符串在生成可执行文件的过程中被转换成了本地编码。

那么和乱码有什么关系呢?
我们知道,Qt内部是使用Unicode编码的,即QString保存的是Unicode编码的字符串。所有使用QString的函数都认为QString内部是Unicode字符串。
那么,当test.exe执行时,会读取”百度”字符串,并使用QString::fromLatin1将字符串转换为QString,拉丁文即英文,很显然转换中文会乱码。通过修改QTextCodec::setCodecForLocale(codec);可以修改这个默认的转换函数,将目标字符串视为指定编码的字符串,再转换为QString。

那么,那些QTextCodec类以及QString::fromLocal8bit的作用和原理是什么呢?

先说QSring::fromLocal8bit吧,这个比较简单,意思就是从一个本地编码的字符串生成一个QString字符串(Unicode),这样的话,将该字符串传递给其他函数,就不会出现乱码了。实验之,首先修改程序如下:

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str_hanzi("百度"); // 汉字
    QString str_ascii("baidu.com"); // 字母

    qDebug() << QString::fromLocal8Bit("百度"); // 修改了此处
    qDebug() << str_ascii;

    return a.exec();
}

输出结果:
这里写图片描述
可以看到它实现了汉字的正常的显示,和理论相同。

那么QTextCodec这个类是做什么的呢?
顾名思义,它的中文名应该是”文本编码转换器”(Text coding converter)或者”文本编解码”(Text code decode),下面是网上流传的一段代码:

QTextCodec *codec = QTextCodec::codecForName("GBK");
QTextCodec::setCodecForTr(codec);
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec);

下面修改程序:

#include <QCoreApplication>

#include <QDebug>
#include <QTextCodec>
#include <QFile>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QTextCodec *codec = QTextCodec::codecForName("GBK");
    QTextCodec::setCodecForTr(codec);

    QString str_hanzi("百度"); // 汉字
    QString str_ascii("baidu.com"); // 字母


    qDebug() << QString::fromLocal8Bit("百度");
    qDebug() << str_ascii;
    qDebug() << QObject::tr("百度");

    return a.exec();
}

输出结果:
这里写图片描述

可以看到,这种方法也能实现正确显示汉字。
但是这种方法的原理是什么?
关键在于QObject::tr()函数。它是翻译函数(translate),同时会进行编码转换。它的默认行为是认为传入的参数是Unicode编码,不需要转换。当你在程序中添加了设置tr编码格式的代码(如上面的程序,设置为GBK)时,会导致编码从你指定的编码(GBK)转换成Unicode。如果把上面的程序中的GBK改成UTF-8,则会乱码,因为该字符串的真正编码是本地编码GBK。实验结果如下图:
这里写图片描述

同理,如果将本地编码设置成其他编码,修改代码如下

#include <QCoreApplication>
#include <QDebug>
#include <QTextCodec>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForLocale(codec);

    QString str_hanzi("百度"); // 汉字
    QString str_ascii("baidu.com"); // 字母


    qDebug() << QString::fromLocal8Bit("百度");
    qDebug() << str_ascii;
    qDebug() << QObject::tr("百度");

    return a.exec();
}

这样的话,使用QString::fromLocal8bit的转换就会不正确,结果如下图:
这里写图片描述

出乎意料的是,tr的转换也出错了。将UTF-8改回GBK,tr也还是乱码:
这里写图片描述

于是添加一行QTextCodec::setCodecForTr(codec);,则显示正确。它们之间会相互影响,这是我们没有预料到的,不过还好这不是什么大问题,可以通过显式设置来纠正。

结论

说了那么多,总结如下:
1、exe中的字符串编码始终是本地编码,与源代码文件的编码无关。
2、Qt内部需要使用Unicode编码的字符串才能正确处理(显示等操作)。
3、由于二者不同,所以对于汉字来说,必须经过转换,第一是通过QString::fromLocal8bit函数来转换,第二是通过QTextCodec来转换。但是setTextCodecForTr、setTextCodecForLocale在高版本已被移除。

注意:
上述测试使用的是Qt4.8.5 msvc2010的版本。minGW还未测试,如果能直接在生成exe文件时直接生成Unicode字符串,那就不需要转换了。

另外还有一种防止乱码的方法,此种方法也可以解决国际化问题导致的乱码,就是使用翻译文件。在源码中统一使用英文。在翻译文件中实现不同版本的语言。

不得不提最后一种比较高级的方法:QStringLiteral宏。它可以直接生成Unicode字符串保存在可执行文件中的只读区域。这样运行时不会发生任何转换。可以显著提高程序运行效率。
测试代码如下:

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str_ascii("baidu.com"); // 字母

    qDebug() << QStringLiteral("a百度a");
    qDebug() << str_ascii;

    return a.exec();
}

生成的可执行文件再也找不到”百度”二字了。

Qt帮助文档中说,QStringLiteral需要编译器支持,如支持C++11就具有这种特性。Qt高版本一般也支持。具体性能方面的影响请看Qt的帮助文档。


本文原创首发于公众号Qt未来工程师,点此查看原文,转载请注明出处。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145522.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Android Studio 4.0.+NDK .so库生成打包

    Android Studio 4.0.+NDK .so库生成打包本篇博文主要讲解了使用AndroidStudio4.0.+生成.so文件详细过程,文中有通过Cmake自动生成和传统生成两种打包.so文件的方案。各方案有详细的图文介绍,保证一看就懂不学就会。对于NDK开发的进阶学习请继续阅读我的NDK系列博文。

  • keypad 错误

    keypad 错误always@(posedgeCLKornegedgenRESET)beginif(!nRESET)beginendelsebeginif(key_en)begincase(key_value_1)7’b0001_001:key_value7’b0010_001:key_value7’b0100_001:key_valu

  • IDEA maven项目打jar包方式总结

    IDEA maven项目打jar包方式总结IDEAmaven项目导出jar包方式总结

  • xgboost分类算法_python分类统计

    xgboost分类算法_python分类统计今天我们一起来学习一下如何用Python来实现XGBoost分类,这个是一个监督学习的过程,首先我们需要导入两个Python库:importxgboostasxgbfromsklearn.metricsimportaccuracy_score这里的accuracy_score是用来计算分类的正确率的。我们这个分类是通过蘑菇的若干属性来判断蘑菇是否有毒的分类,我们来看看数据…

  • laravel 安装完成后安装 vendor 目录

    laravel 安装完成后安装 vendor 目录

    2021年10月20日
  • FDD 与 TDD LTE区别

    FDD 与 TDD LTE区别虽然国内4G牌照迟迟不见发布,但私底下各大运营商和终端商早已摩拳擦掌,各网用户尤其是备受折磨的中国移动用户也都在期待4G时代的到来。放眼望去,目前在全球81个国家已有213张LTE商用网络,其中FDD-LTE商用网络192张、TD-LTE商用网络11张,而中国4G如箭在弦,被认为是一场由中国移动积极促成的产业大跃进。随着大家不断谈论4G,相关的新名词也不断涌现,包括不同制式的网

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号