彻底弄懂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未来工程师,点此查看原文,转载请注明出处。

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux之fstab文件详解「建议收藏」

    linux之fstab文件详解「建议收藏」/etc/fstab是用来存放文件系统的静态信息的文件。位于/etc/目录下,可以用命令less/etc/fstab来查看,如果要修改的话,则用命令vi/etc/fstab来修改。当系统启动的时候,系统会自动地从这个文件读取信息,并且会自动将此文件中指定的文件系统挂载到指定的目录。下面我来介绍如何在此文件下填写信息。

    2025年8月9日
    3
  • jar中没有主清单属性 (亲测有效)[通俗易懂]

    jar中没有主清单属性 (亲测有效)[通俗易懂]<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></.

    2025年9月14日
    8
  • Java数组超详解

    Java数组超详解一、前言前面我们学习了随机数的介绍和使用,那么这篇我们来学习java中数组的定义和使用,java的数组和c语言的十分类似。二、数组的定义数组定义的形式:格式1:数据类型[]数组名;如int[]arr;说明:定义了一个int类型的数组,数组名是arr格式2:数据类型数组名[];如intarr[];说明:定义了一个int类型的数组名是arr的数组…

    2022年7月14日
    18
  • java 滤波算法_双边滤波算法

    java 滤波算法_双边滤波算法1、原理高斯滤波是以距离为权重,设计滤波模板作为滤波系数,只考虑了像素间的空间位置上的关系,因此滤波的结果会丢失边缘的信息。高斯滤波的缺陷如下图所示:平坦区域正常滤波,图像细节没有变化,而在突变的边缘上,因为只使用了距离来确定滤波权重,导致边缘被模糊。在高斯基础上,进一步优化,叠加了像素值的考虑,因此也就引出了双边滤波,一种非线性滤波,滤波效果对保留边缘更有效。为了理解双边滤波的距离和像素差两个影…

    2022年5月29日
    39
  • MFC 如何让菜单返灰(不可点击状态)

    MFC 如何让菜单返灰(不可点击状态)

    2022年2月7日
    43
  • 如何从Java官网下载 Java API 文档

    如何从Java官网下载 Java API 文档1 登录 Oracle 官网 https www oracle com 2 进入 Java 页面 3 将页面拉到底部 4 将页面拉到中间 5 6 7 将页面下拉 8 9 1011 下载完成后解压到任意目录 打开 doc api 目录 打开 index 页面 尽情享受即可

    2025年9月20日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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