从此乱码是路人
相信大家在刚学 Qt的时候一定遇到过
#include
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel lb;
lb.setFont(QFont("Sans Serif", 24));
lb.setText(" 乱码其实是一件很容易的事情。 ");
lb.show();
return a.exec();
}
学会了第一个 ...
相信大家在刚学 Qt的时候一定遇到过
#include
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel lb;
lb.setFont(QFont("Sans Serif", 24));
lb.setText(" 乱码其实是一件很容易的事情。 ");
lb.show();
return a.exec();
}
学会了第一个 Qt 例子,
很高兴,
于是想要做一点改变。
兴冲冲地输入一段比如
“XXX 是大神”的句子,
结果却发现,
显示的是乱码。
由于字符编码设置不当,
导致应用程序无法正常显
示文字,称之为乱码。
以之问老师,老师给你三行代码
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
当然,在此我也就不解释为什么中间那行
实际上是没有任何作用的了。不过你们真
的这么放心这三行代码嘛?
这三行代码,真的管用吗?
事实上,这三行代码,
是相当错误的打开方式!
让我们来看一个Windows7下的例子
这看上去很美好,不是么?
但是当我们使用非中文的Windows时
这究竟是为什么?
● 其实不仅仅是我们的程序,在其他很多地方都有乱码。
● 打开一个网页,结果显示出来的是不知所云的外星文字。
● 非英语国家的软件,曾经有过那么一段水火不容的历史。
● 玩过日文游戏的人,可能都接触过 AppLocale这个神器。
● 还有 9#论坛分为 GBK和 BIG5两种编码环境。
● ……
这是一个很复杂的历史问题
首先,我们得承认有以下一些事实
● 计算机只能存储二进制位。 8个二进制位是 1个字节。 1
个字节可以示 0~ 255之间的任何一个数字。
● 文件,无论是「文本文件」还是「音乐文件」,或者是所
谓的「二进制文件」,本质上都只是一连串 0~ 255的数
字。这个序列被称为字节流。
● 因此,我们眼中的「字符串」,在计算机看来,也不过是
一个字节流而已。
● 表示「字符串」的过程,就是将字符映射为字节的过程。
美国人发明了 ASCII字符编码
英文字母很少,一
个字节就能全部囊
括,因此美国人只
发明了字节和英文
字符之间的一一映
射关系。
这就是 ASCII 。
然后人们发现一个字节不够用了
● 欧盟 27个国家有大量拉丁语系的字符,远远超过了 256
所能容纳的数量。
● 作为亚洲语言的代表,中日韩语言中所使用的汉字,仅仅
常用便已达到了数千。而截至清代,《康熙字典》已收录
汉字 47000有余。
● 更不用提其他各种小语种的文字。
● ……
既然一个不够,我们就来俩
● 在美国人的 ASCII编码系统中, 128~ 255之间的数字几
乎没有使用。而凡是小于等于 127的数字,二进制表示中
首位都是 0;大于等于 128的数字,首位都是 1。
● 这个特性很快被得到了运用。一般的想法是,在字节流
中,如果出现大于等于 0x80的字符,则被认为(可能)
是多字节编码的「前导字节」,需要同下一个字节一起构
成完整的「字符」。
● 凭借这个,各个国家先后制定了自己的「多字节编码
规则」。比如简体中文第一个国家编码,就是 GB2312。
你埋怨我不懂你,你又何曾懂过我
● 每个国家的文字编码「各自为政」,这导致不同国家
之间交换信息变得非常困难。传输文档之前要实现约
定双方使用的编码方式;当然这不是唯一的问题。
● 更大的问题在于,你甚至不可能在一篇文档里,同
时插入德语字母和中文汉字!于是你在打印一篇语
言混用的文章的时候,很可能需要将文章分批打印,
然后把他们全部粘起来?!
● ……
我们还是坐下来好好研究一下吧
于是 Unicode诞生了
● 20世纪 80年代末, Unicode统一码联盟成立。
● 1991年十月发表第一个 Unicode,即 Unicode 1.0。
● 2001年 3月, Unicode 3.1收录超过 65536个字符,正
式由双字节编码扩展为四字节编码。
● 2005年 3月, Unicode 4.1收录了第十万个字符,其为马
来亚拉姆语的 Praslesham( പ്രേശല്ഷം )。
● 2012年 1月 31日, Unicode 6.1发布。
UCS-2, UTF-8和 UTF-16
● 事实上刚开始的时候, Unicode编码曾经只有 65536个字符空间。这个空间
被称为「基本多文种平面」。用恒定的两个字节表示所有字符,这种编码方
式被称为 UCS-2。
● 当 Unicode字符超过 65536之后,参考「多字节编码」,通过双字节扩展四
字节的方式表示所有字符。这种编码方式被称为UTF-16。
● 为了便于网络传输和数据处理,不至于因为遇到“ \0”而错误地以为文本结
束,以及兼容 ASCII码, UTF-8被定义。 UTF-8的每一个字符按规则编码
为 1~ 4任一字节组合。中文通常为 3字节长。
● 当然,也有直接使用 4字节编码所有字符的编码方式,被称为 UTF-32或者
UCS-4。
用 GBK和 UTF-8编码的中文
Windows的历史遗留问题
● 在 Unicode尚未成熟的年代,Windows是使用各个国家的多
字节编码,来支持每个国家的语言的。
● 自 WinNT 发布之后, Windows 内核的 API 全部改成使用
UTF-16的编码方式,以更好地支持多语言。但是由于历史原
因, Windows仍然保留多字节编码的 API。 UTF-16的 API
以W结尾,而多字节编码以 A结尾。
● 例如 CreateWindowA和 CreateWindowW。
● 但是直到现在为止,很多Windows程序员仍然没有 Unicode
字符编码的相关知识,因此写出来的程序,就很容易乱码……
说了这么多,
也该解决之前提到的问题了。
让我们先研究一下 C++的字符串常量
● 编译器处理字符串常量,只是把引号里面的字节数组全部
复制,待程序运行的时候变成内存中的字节数组而已。
● 因此以下几行等价(以 UTF-8编码保存 C++源文件):
const char s1[] = " 最喜欢 C++ 了! ";
const char s2[] = "\xe6\x9c\x80\xe5\x96\x9c\xe6\xac\xa2\x43\x2b"
"\x2b\xe4\xba\x86\xef\xbc\x81";
const char s3[] = { 0xe6, 0x9c, 0x80, 0xe5, 0x96, 0x9c, 0xe6, 0xac,
0xa2, 0x43, 0x2b, 0x2b, 0xe4, 0xba, 0x86, 0xef,
0xbc, 0x81, 0x00 };
然而 QString使用的是 UTF-16
● std::string 本质上是字节数组,因此从 const char* 转换为
std::string不会有太大问题。以前大家写程序的时候不注意这方
面的东西,保存的文件自然也都是 GBK 的,在中文版
Windows下面也看不出什么问题。
● 但是 Qt不同。 Qt从诞生开始就是国际化的项目,因此特别注
重这些东西。在兼顾了计算性能和存储性能之后, Qt小组决定
将 UTF-16作为 QString的编码格式。
● 所以诸位源代码中出现的中文字符串常量,在被 Qt使用之前,
都经历了一次由「多字节编码」转换为 UTF-16的过程。
源文件中的「多字节编码」是什么?
● 如果不特别说明, Qt 4.x会认为源文件的编码是 Latin-1(西欧语言多
字节编码)。当然, Qt 5.x已经修正这个坏习惯,默认源文件是 UTF-8
来着。不过谁叫我们现在还在用 4.8呢?
● 就像 std::string做的一样, QString可以在必要的时候由 const char*隐
式 或 显 式 转 换 而 成 。 这 个 过 程 中 使 用 的 编 码 , 就 是
QTextCodec::codecForCStrings。
● Qt有一个函数, QObject::tr,也可以将 const char* 转换为 QString。
这个过程中使用的编码方式由 QTextCodec::codecForTr指定。
● 另外 QTextCodec::codecForLocale表明当前系统所用编码。一般中文
Windows应该是 GBK。中文 Linux则一般为 UTF-8。
但是 Qt Creator保存源文件的编码是
System!也就是 codecForLocale!
GBK的源文件自然需要 GBK解码
碰 巧 中 文 Windows 的 codecForLocale 是
GBK ,因此在中文系统下面完全没有压力。
但是在非中文系统下就成了个悲剧
尝试用 US-ASCII解码 GBK字符串 尝试用 SHIFT-JIS解码 GBK字符串
这个时代用 GBK写程序就应该
让我告诉你们正确的打开方式
和我签订契约,使用 UTF-8吧!
然后改变那三行代码……不,两行代码
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf-8"));
将 QObject::tr 和 const char* 的编码都改成 UTF-8 ,并
且使用 UTF-8 保存文件。这样就可以保证在任何语言的
操作系统上面都可以显示正确的中文。
但是要特别注意的是,修改 codecForLocale要慎重!
在 Qt 里面, codecForLocale 的作用主要有两个,一个
是与外部文件读写的时候使用的默认编码,一个是向命
令行输出信息( qDebug )使用的编码。
于是我们的程序终于不乱码了
Qt的整个框架都使用 UTF-16编码
● 合理遵循 Qt的规则,基本不会出现字符乱码的问题。
● 除了 const char*的隐式转换和 QObject::tr的转换之外,
Qt事实上有更一般的字符编码转换方式。
// 我们的源文件是 UTF-8 格式的,因此建立一个 QString 。
QString s = QString::fromUtf8(" 这是 UTF-8 的字符串 ");
// 将 QString 转换为 GBK 格式的 QByteArray 字符数组。
// QByteArray::data() 可以拿到 const char* 。
QByteArray gbk_s = QTextCodec::codecForName("gbk")->fromUnicode(s);
// 然后再从 GBK 字符数组转换回 QString 。
QString s2 = QTextCodec::codecForName("gbk")->toUnicode(gbk_s);
有关文件 IO的编码处理
● 事实上 Qt 读取文件可以用任何一种编码,只是默认以
codecForLocale而已。
● 使用 QTextStream 可以通过文本方式读取 QFile ,而
QTextStream则可以设定使用的编码方式。
● 就算文件名有中文也不怕。只要正确转换为 QString,剩
下的就交给 Qt吧!
QFile file(QString::fromUtf8("中文名 .txt"));
if (file.open(QIODevice::ReadOnly)) {
QTextStream fin(&file);
fin.setCodec(QTextCodec::codecForName("utf-8"));
fin.readLine();
}
反而是 QObject::tr不应滥用
● 可能有不少人随手写过这样的代码吧:
tr("新建文本文档 .txt")
● 能用 tr轻易解决中文乱码,这似乎很方便。但是 tr绝不应
该用在这种地方!使用 tr标记的文本,可以被 Qt配套的工
具扫描出来,然后交给「精通各国外语的牛人」进行翻译。
● 翻译完成之后, QObject::tr函数就会在已经加载的翻译文
件中,查找这个字符串的翻译。如果找到,就会用翻译好的
字符串替换原来的字符串。
● 那么,文件名算什么「需要翻译的字符串」啊亲?
QObject::tr的真正作用
恩,似乎问题都解决了?
其实本来我还有很多话题的
● 比如在没有 Qt的情况下,怎么在 C++程序里正确处理 UTF-8。
( Windows 环 境 下 面 使 用 MultiByteToWideChar 和
WideCharToMultiByte 这两个 API 。 Linux 嘛,这年头不用
zh_CN.UTF-8简直没脸见人了……)
● 比如 C++11的 UTF-8支持,还有 Qt 5.x的一些改变。
● 比如在 Python里面怎么正确处理 UTF-8, GBK和 UTF-16。
(其实 Python 2.x编码处理更麻烦,因为 2.x对于 str和 unicode
两个类型会动不动自动转换,而且默认编码还改不了。有些函数
对于 str和 unicode有不同返回值,行为又诡异,会让你想哭!)
● 比如 HTML文件的编码, URL 的编码之类的。
不过,还是算了吧~
页 1
页 2
页 3
页 4
页 5
页 6
页 7
页 8
页 9
页 10
页 11
页 12
页 13
页 14
页 15
页 16
页 17
页 18
页 19
页 20
页 21
页 22
页 23
页 24
页 25
页 26
页 27
页 28
页 29
页 30
页 31
页 32
页 33
页 34
页 35
页 36
页 37
本文档为【从此乱码是路人】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。