求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
Qt 编程指南
第1章 Qt 开发环境
1.1 Qt 介绍
1.2 Qt 下载
1.3 Qt 在 Windows 下安装
1.4 Qt 在 Linux 下安装
1.5 认识开发工具
1.6 常见的名词术语
第2章 从Hello World开始
2.1 Hello World
2.2 Hello Qt
2.3 Hello Designer
2.4 Hello Creator
2.5 Qt程序调试
2.6 Qt帮助文档
第3章 字符串和字符编码
3.1 字符编码方式
3.2 Qt 程序字符编码
3.3 使用 QString
3.4 使用 QByteArray
第4章 信号和槽函数
4.1 元对象系统
4.2 使用原有的信号和槽
4.3 自定义信号和槽
4.4 系统属性
4.5 扩展阅读:ui_*.h代码
4.6 扩展阅读:moc_*.cpp代码
第5章 简单控件的使用
5.1 按钮类的控件
5.2 单行编辑控件
5.3 丰富文本编辑控件
 

 
目录
字符编码方式
21 次浏览
1次  

本节对操作系统里的字符编码进行简单介绍,这对于程序的跨平台开发、汉字乱码问题解决等都是很重要的知识,了解这些知识就不再害怕遇到程序乱码的问题了,本节最后 测试不同字符编码的源文件的编译运行情况。

1. ANSI多字节编码

在最早的时期,计算机只支持英文字符,那时都是用 ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)编码,一个字母或符号只需要一个字节存储。随着计算机的推广应用,越来越多的国家和地区面临本地语言文字如何在计算机里使 用和显示的问题。对于中文 DOS 系统和早期的中文 Windows 系统,大陆制定了国标码 GB2312,台港澳地区则使用了大五码 Big5。微软针对这些本地化字符编码采用的就是用 ANSI(American National Standards Institute,美国国家标准学会)多字节编码方式,系统里的英文和符号就使用单字节的 ASCII(0x00~0x7f),而对于汉字之类的本地化字符编码,就采用 0x80~0xFF 范围内的多个字节来表示,这样既能兼容 ASCII ,又能正常使用本地化语言文字。大陆的国标码发展了好几代,归结如下:

  • GB2312:1980年发布,收录了7445个字符,包括6763个汉字和682个其它符号。汉字是双字节编码。
  • GBK:1995年发布,收录了21886个符号,包括21003个汉字和883个其它符号。汉字是双字节编码。简体中文 Windows 目前默认采用这种本地化编码。
  • GB18030-2000:2000年发布,收录了27533个汉字,汉字分为双字节编码部分和四字节编码部分。
  • GB18030-2005:2005年发布,收录了70244个汉字,汉字也分为双字节编码部分和四字节编码部分。

新的国标码标准通常是兼容旧的编码方式的,所以一般对简体中文的文本选择 GBK 或 GB18030 编码都是可以正常显示的。微软针对各种本地化语言的页面有自己的编号方式,GBK 对应的代码页编号是 936,GB18030 对应的代码页编号是 54936,Big5 对应的代码页编号是 950。

关于汉字编码可以参考

关于代码页编号可以参考

2. Unicode系列编码

ANSI 多字节编码解决了各种语言文字的本地化使用问题,也有它自己的缺陷:各地制定的编码标准只对自己的语言文字有效,而不同语言文字的编码都是冲突的,因为大家都用 0x80~0xFF 范围字节表示自己的语言文字,而不考虑别的语言文字如何编码,冲突在所难免。比如简体中文(GBK)的文本放到繁体中文(Big5)的操作系统里,就被默认解析成繁体字编 码,两种编码是冲突的,就会显示混乱的繁体字,反过来也一样。

因此国际组织制定了 Unicode 编码,也叫万国码、国际码等,这种字符编码是对全球语言统一分配编码区间,各种语言字符互相不冲突,都可以兼容使用。Unicode 编码从最初的 1.0 ,慢慢发展到今年发布的 8.0,包含的语言文字越来越多。

Unicode 编码系统,可分为编码方式和实现方式两个层次。对于国际组织发布的 Unicode 编码标准,对应的就是编码方式,最常用的是 UCS-2(Universal Character Set 2),采用两字节编码一个字符。当然国际语言文字太多,两字节不够用了,就有四字节编码方式 UCS-4。这个仅仅是标准,而不是实现,在编码实现的过程中,有些考虑兼容旧的单字节 ASCII 编码,有些不考虑兼容性;有些考虑双字节中哪个字节放在前面,哪个字节放在后面的问题,即 BOM(Byte Order Mark,字节顺序标记)的作用。因此诞生了多种国际码的实现方式,统称为 Unicode 转换格式(Unicode Transformation Format,UTF):

  • UTF-8:灵活的变长编码,对于 ASCII 使用一个字节编码,其他本地化语言文字用多个字节编码,最长可以到 6 个字节编码一个字符。对于汉字,通常是 3 个字节表示一个汉字。这是 Unix/Linux 系统默认的字符编码。
  • UTF-16:兼容 UCS-2,一般都是两字节表示一个字符,对于超出两字节的国际码字符,使用一对两字节来表示。在存储时,按两个字节的排布顺 序,可以分为 UTF-16LE(Little Endian,小端字节序)和UTF-16BE(Big Endian,大端字节序),微软所说的 Unicode 默认就是 UTF-16LE。
  • UTF-32:同 UCS-4,因为用四个字节表示一个字符,所以不需要考虑扩展了。这种编码方式简单,但也特别浪费空间,所以应用很少。在存储时也分为 UTF-32BE 和 UTF-32LE,因为用得少,所以不用太关心这种编码格式。

关于 Unicode 编码格式参考

关于 UTF 和 BOM 参考

3. C++使用的字符串

在 C++ 中,以前通常使用 char 表示单字节的字符,使用 wchar_t 表示宽字符,对国际码提供一定程度的支持。 char * 字符串有专门的封装类 std::string 来处理,标准输入输出流是 std::cin 和 std::cout 。对于 wchar_t * 字符串,其封装类是 std::wstring,标准输入输出流是 wcin 和 wcout。虽然规定了宽字符,但是没有明确一个宽字符是占用几个字节,Windows 系统里的宽字符是两个字节,就是 UTF-16;而 Unix/Linux 系统里为了更全面的国际码支持,其宽字符是四个字节,即 UTF-32 编码。这为程序的跨平台带来一定的混乱,除了 Windows 程序开发常用 wchar_t* 字符串表示 UTF-16 ,其他情况下 wchar_t* 都用得比较少。

MFC 一般用自家的 TCHAR 和 CString 类支持国际化,当没有定义 _UNICODE 宏时,TCHAR = char,当定义了 _UNICODE宏 时,TCHAR = wchar_t,CString 内部也是类似的。 Qt 则用 QChar 和 QString 类(内部恒定为 UTF-16),一般的图形开发库都用自家的字符串类库。

在新标准 C++11 中,对国际码的支持做了明确的规定:

  • char * 对应 UTF-8 编码字符串(代码表示如 u8"多种文字"),封装类为 std::string;
  • 新增 char16_t * 对应 UTF-16 编码字符串(代码表示如 u"多种文字"),封装类为 std::u16string ;
  • 新增 char32_t * 对应 UTF-32 编码字符串(代码表示如 U"多种文字"),封装类为 std::u32string 。

因为 Qt 有封装好的 QString,所以不太需要这些新增的字符串格式。 关于 C++11 字符串更详细的内容参考

4. 源文件字符编码测试

在 Windows 系统里最常用的文本字符编码格式是 ANSI (简体是 GBK,繁体是 Big5)和 Unicode (UTF-16LE)格式,Windows 命令行默认的输入输出格式是 ANSI 的。在 Linux 系统里统统都是 UTF-8 ,这个比较省心。Windows 自带的记事本,以及 Notepad2 工具、Linux 系统里的 kwrite、gedit 等都支持各种编码格式的文本显示。以 Windows 记事本为例,打开任意一个文本文件,选择菜单“文件”

-->“另存为”,点开“另存为”对话框最下面的“编码”:

textcodec

图上四种格式分别是:

  • ANSI:本地化语言文字编码,简体操作系统是 GBK,繁体操作系统就是 Big5,Windows 命令行默认用的字符编码是和这个一样的。
  • Unicode:Windows 所说的国际码一般都指这个 UTF-16LE,文件开头带有 BOM 标记(2字节)。
  • Unicode big endian:即 UTF-16BE,这个很少用到,文件开头也带 BOM 标记(2字节)。
  • UTF-8:这是 Unix/Linux 系统里通用的国际码存储交换格式,记事本保存的文件开头会有 UTF-8 签名(3字节,类似 BOM)。

下面我们测试三个不同编码格式的 cpp 文件,查看其编译运行效果。打开本教程示例代码的网址,进入ch03/textcodec 子文件里面,下载里面的压缩包 textcodec.7z,解压缩到D:\QtProjects\ch03\textcodec 文件夹里。

解压后可以看到 GBK.cpp、UTF-8.cpp、UTF-16.cpp 三个文件,如文件名所述,第一个是 ANSI+GBK 编码的源文件,第二个是 UTF-8 编码的源文件,第三个是 UTF-16LE(Windows默认的Unicode)编码的源文件。用记事本打开查看它们的内容是完全一样的:

#include <iostream>
#include <cstring>
using namespace std;

int main(int argc, char *argv[])
{
char theStr[] = "1234打印汉字";
cout<<"str length: "<<strlen(theStr)<<endl;
cout<<theStr;

return 0;
}

注意看文件存储的字节数:

  • GBK.cpp 是 231 字节,231 = 2*4(汉字) + 223 (其他英文和符号);
  • UTF-8.cpp 是 238 字节,238 = 3*4(汉字)+ 3(文件头签名)+ 223 (其他英文和符号);
  • UTF-16.cpp 是 456 字节,456 = 2*4(汉字)+ 2(BOM)+ 223*2 (其他英文和符号)。

然后我们在 Qt 命令行尝试用 g++ 编译这些代码,并运行生成的程序,挨个测试:

g++ GBK.cpp -o GBK

textcodec

 

g++ UTF-8.cpp -o UTF-8

textcodec

 

g++ UTF-16.cpp -o UTF-16

textcodec

第一个 GBK 编码的程序是正常的,那是因为作者使用的 Windows 命令行默认的代码页是 936 ,命令行里的输入输出都是 GBK 编码的,与源文件编码格式一致,所以显示正确。

第二个 UTF-8 正常编译,但是源文件里的 UTF-8 汉字编码与 Windows 命令行代码页编码格式不符合,所以显示的汉字乱码,而数字是没问题的。UTF-8 和 GBK 都是兼容 ASCII 单字节编码的。

第三个 UTF-16 编译根本不成功,因为 UTF-16 里面一个字符用双字节表示,导致源文件有大量的数值为 0 的字节(被编译器认为是 null),因此编译失败了。这也是 UTF-16 不兼容 ASCII 单字节编码的弊端。

如果在 Linux 系统里用 g++ 编译上面三个文件,结果是 GBK 编码程序可以编译,运行乱码;UTF-8 编码程序可以编译,运行正常;UTF-16 编码程序是无法通过编译的。Linux 系统里命令行默认的输入输出都是 UTF-8 编码,所以只有 UTF-8 的程序才会正常。GBK 的能正常编译是因为兼容 ASCII 单字节编码的缘故,无论是英文变量名还是符号,都与 UTF-8 的编码一致,只有非 ASCII 字符才会显示不正常。

那么 UTF-16 的源代码谁能正常编译呢?答案就是微软出的 Visual Studio,微软自家用的 Unicode 还得靠 MSVC 编译器来编译:

cl UTF-16.cpp /EHsc

textcodec

可以看到 MSVC 编译器是能正常编译 UTF-16 的源文件,显示也很正常。感兴趣的读者可以用 MSVC 编译器(VS2010 SP1以上版本)把三个源文件都测试一遍,可以发现 MSVC 编译器生成的 exe 运行都是正常显示汉字的,这是为什么呢?

可以大致理解为:

①g++ 编译器不会对源文件里的字符串做转码。上面 g++ 生成的 GBK.exe 与 UTF-8.exe 二进制文件内容是有多处差异的。在 Windows 系统里源文件是 GBK ,其命令行编码也是 GBK,所以 GBK 编码的程序正常显示汉字;在 Linux 系统里源文件是 UTF-8,其命令行编码也是 UTF-8,所以 UTF-8 编码的程序正常显示汉字。

②MSVC 编译器会根据不同的源文件编码格式进行转码,文件中 char * 字符串一律转成本地化的 ANSI 多字节编码, wchar_t * 字符串一律转成 Unicode(UTF-16LE)。用 MSVC 生成的 GBK.exe、UTF-8.exe、UTF-16.exe 三个二进制文件,除了 PE 文件头有几字节区别,程序主体就是完全一样的,这三个 exe 内部的字符串全是 ANSI 多字节编码的,当然都能正常显示汉字。

从跨平台运行的角度,我们推荐 UTF-8 编码的源文件,这也是 Qt5 官方规定的源文件格式。下一节我们介绍 Qt 程序里的字符编码函数,无论是打印到操作系统命令行还是 QtCreator 的输出显示面板,我们都可以让汉字正常显示。

tip 练习

① 仔细查看本节用 g++ 编译生成的 GBK.exe、UTF-8.exe 和 MSVC 编译生成的 UTF-16.exe 打印显示的 str length 数值,列出这三个数值的计算公式(与三个 cpp 文件长度计算公式对比看看)。

② 如果有 VS2010 SP1 以上的开发环境,在命令行编译本节三个源文件,然后可以用 VS 自带的 Microsoft Windows SDK Tools 里的 WinDiff 工具对比 cl 编译器生成的 exe 文件,每次比较两个,看看二进制文件之间的差异。


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



21 次浏览
1次