所谓字符编码方案就是将所需编码的符号顺序编号,统一用一个序列号来表示。编码时当然会适当考虑分类的原则,即尽量将同类的字符放在一起,以相同的二进制开头,如ASCII的a-b就分别对应十进制的序号:97-122,97的二进制是0110 0001。

对于字符的数据表示使用编码方案(字符用顺序号编码),而对于字符的输出则一般是用点阵图形来输出的。而对于输入来说,英文就简单了,直接按键盘输入即可,中文则不可能直接使用编码方案的顺序号来输入,因为那样不够直观,不方便记忆和操作,为此,搞出来不同的输入方案,如五笔、拼音输入法。对于存储呢?简单的方法当然是以最大的编码(顺序号)所需的字节数来存储,但对于需要多个字节的编码方案来说,存储编码(顺序号)在最前面的部分显然并不需要最大的字节数,所以对于多字节编码的方案往往会按一定规则另外搞出一套存储方案,然后按规则做适当的转换即可,这是一种以时间换空间的做法。

1 ASCII

因为计算机是美国人最先搞出来的,而美国人使用的符号少,一个字节(8个位,0-11111111)就足够了,这就是ASCII,包括大、小定英文字母、数字、一些可打印或不可打印的其它符号,共128个:

c语言中字符串的各种指令(C深入理解编程中的字符编码)(1)

2 GB2312

中国使用的汉字需要以万计,常用的汉字也是以千计,一个字节远远不够。(两个字节16个位(0x0-0xffff)理论上可以表示2^16=65536个字符。)编码需要考虑与ASCII兼容,且方便使用。

2.1 兼容ASCII。

2.2 由两个大于127的字节来映射我们增加的字节(确保两个字节的最高位都是1);

2.3 发现最高位是0,那么就可以确定这个字节映射的是ASCII的字符,那么就只读取一个字节,然后去查ASCII表,将其解析为ASCII表对应的字符。发现最高位是1,那么就可以确定这个字节映射的就是GB2312的字符,那么就连续读两个字节,然后去查GB2312,然后将其解析为对应的字符。

2.4 ASCII重新映射了两个字节的ASCII,这些称为全角字符,而本身的ASCII的一个字节映射的字符,称为半角字符。

2.5 后面又增加了一些字符,形成了GBK,然后再增加了一些,形成了GB18030,都向前兼容。

2.6 ANSI在不同操作系统中,对应不同的字符编码,如简体中文的操作系统,对应GB2312,繁体的对应Big5,日语的操作系统对应JIS。

GB 2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时,GB 2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

GB 2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。

对于人名、古汉语等方面出现的罕用字,GB 2312不能处理,这导致了后来GBK及GB 18030汉字字符集的出现。

GB 2312以编码0xA1A0(10100001 10100000)开始:

c语言中字符串的各种指令(C深入理解编程中的字符编码)(2)

3 unicode

如果每个国家都搞自己的一套字符编码方案,对于信息通信自然不便,为此,国际标准化组织便搞了一个统一的unicode字符编码方案,尽可能包含各国使用的字符。

UCS-2用2个字节编码,UCS-4用4个字节(0x0-0xffffffff)编码(理论上2^32=4294967296)。

unicode本身只规定了对应的二进制数是多少(也是一个序列号)。因为需要更多的位数,但发现一些编码在前的序列号使用一个字节或两个字节就行了,如果同样使用4个字节的话,会造成空间浪费,可以考虑另外经济的存储方案。所以在unicode统一数字编码的基础上,再搞一套存储的编码方案,这就是utf,以用时间来换空间,或用空间换时间,或折衷,对应utf-8、utf-32、utf-16,因为存储的是另外一套编码体系,需要再做转换。转换格式分别是:

utf-8

不同的编码段使用不同的字节数(1-6)存储,一位的以0开关,两位的段以110和10开头,三位的以1110和10、10开头,四位的以11110和10、10、10开头。这样,编码在前的字符存储时就节省了空间,是一个以时间换空间的折衷方案。

c语言中字符串的各种指令(C深入理解编程中的字符编码)(3)

utf-16

使用两个字节或者4个字节来存储,分两个段,第一个段则直接存储,不转换,使用两个字节,如果属于第二个段(高段),第1、2个字节以110110开头,第3、4个字节的以110111开头,其它的以unicode编码依次填入。

c语言中字符串的各种指令(C深入理解编程中的字符编码)(4)

utf-32:固定长度,使用4个字节存储,以空间换时间(因为不需要转换了)。

4 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,所以不太需要这些新增的字符串格式。

-End-

,