彻底弄懂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)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 过滤器(Filter)和拦截器(Interceptor)的区别「建议收藏」

    过滤器(Filter)和拦截器(Interceptor)的区别「建议收藏」来自:http://www.cnblogs.com/luoyun/archive/2013/01/04/2844274.html过滤器(Filter)和拦截器(Interceptor)的区别Filter介绍     Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对…

    2022年4月18日
  • Ubuntu安装和卸载CUDA和CUDNN[通俗易懂]

    Ubuntu安装和卸载CUDA和CUDNN[通俗易懂]目录目录前言卸载CUDA安装CUDA下载和安装CUDA下载和安装CUDNN测试安装结果参考资料前言最近在学习PaddlePaddle在各个显卡驱动版本的安装和使用,所以同时也学习如何在Ubuntu安装和卸载CUDA和CUDNN,在学习过程中,顺便记录学习过程。在供大家学习的同时,也在加强自己的记忆。卸载CUDA为什么一开始我就要卸载CUDA…

    2022年6月12日
  • jsp文件开头<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 > 有什么用

    jsp文件开头<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 > 有什么用http://www.w3.org/TR/html4/loose.dtd”> 声明位于文档中的最前面的位置,处于  标签之前。DOCTYPE是DocumentType(文档类型)的简写,此标签用来告知浏览器文档使用哪种 HTML 或 XHTML 规范。Eg:

    2022年7月14日
  • php 禁用 chunked,nginx配置关闭chunked「建议收藏」

    php 禁用 chunked,nginx配置关闭chunked「建议收藏」Transfer-Encoding:chunked简介通常情况下,Transfer-Encoding域的值应当为chunked,表明采用chunked编码方式来进行报文体的传输。chunked编码是HTTP/1.1RFC里定义的一种编码方式。chunked编码的基本方法是将大块数据分解成多块小数据,每块都可以自指定长度。Transfer-Encoding:chunked能更快的让页面呈现出来,因…

    2022年11月3日
  • 使用SecureCRTPortable向Linux上传文件

    使用SecureCRTPortable向Linux上传文件1.打开SecureCRTPortable2.点击上册菜单栏文件->连接SFTP会话(S)3.上传文件sftp>put-r”本地文件目录\文件名”4.输入完成后点击回车,会将文件上传到Linux的当前用户的home目录下解析:如果你是用root连接的sftp,那么上传的文件就会保存到/root目录下…

    2022年6月9日
  • Statement 和 PreparedStatement之间的关系和区别

    Statement 和 PreparedStatement之间的关系和区别关系:PreparedStatement继承自Statement,都是接口 区别:PreparedStatement可以使用占位符,是预编译的,批处理比Statement效率高表示预编译的SQL语句的对象。接口:publicinterfacePreparedStatementextendsStatement之间的继承关系SQL语句被预编译并存储在PreparedStatement对象中。然后可以使用此对象多次高效地执行该语句。注:用于设置IN参数值的设置方法(setShort

    2022年4月28日

发表回复

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

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