ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)
GBK 全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification)
Unicode(统一码、万国码、单一码)
UCS 通用字符集(Universal Character Set)
UTF(Unicode/UCS Transformation Format)
1、ASCII码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。
标准ASCII 码也叫基础ASCII码,使用7 位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。在标准ASCII中,其最高位(b7)用作奇偶校验位。
2、GBK 采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
其中GB 2312 汉字区。即 GBK/2: B0A1-F7FE。首字节在B0-F7之间,尾字节在A1-FE之间,共72个区,每个区94个汉字,共收录 GB 2312 汉字 6763 个(其中D7FA-D7FE 个码位没有定义汉字),按原顺序排列(拼音顺序),B0A1为第一个汉字‘啊’。
3、Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。其中U+4E00~U+9FA5码位,共收入了20902个常用汉字,按笔画顺序排列,U+4E00为第一个汉字‘一’。
4、通用字符集(Universal Character Set,UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,其取值范围为U+0000~U+FFFF,UCS-4用4个字节编码,其取值范围为U+00000000~U+7FFFFFFF,其中 U+00000000~U+0000FFFF和UCS-2是一样的。
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行 (row),每行有256个码位(cell)。group 0的plane0被称作BMP(Basic Multilingual Plane)。将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。每个平面有2^16=65536个码位。Unicode计划使用了17个平面(group0的前17个平面),一共有17*65536=1114112个码位。
5、UCS-2和UCS-4只规定了代码点和文字之间的对应关系,并没有规定代码点在计算机中如何存储。 规定存储方式的称为UTF(Unicode Transformation Format),其中应用较多的就是UTF-16和UTF-8了。
5.1 UTF-16由RFC2781规定,它使用两个字节来表示一个代码点。
UTF-16是完全对应于UCS-2的,即把UCS-2规定的代码点通过Big Endian或Little Endian方式 直接保存下来。UTF-16包括三种:UTF-16,UTF-16BE(Big Endian),UTF-16LE(Little Endian)。UTF-16BE和UTF-16LE不难理解,而UTF-16就需要通过在文件开头以名为BOM(Byte Order Mark)的字符 来表明文件是Big Endian还是Little Endian。BOM为U+FEFF则说明UTF-16BE,BOM为U+FFFE则说明UTF-16LE。UTF-16的表达范围是U+0000-U+FFFF。
另外,UTF-16还能表示一部分的UCS-4代码点——U+10000~U+10FFFF。计算方法如下:
从代码点U中减去0x10000,得到U'。这样U+10000~U+10FFFF就变成了 0x00000~0xFFFFF。
用20位二进制数表示U'。 U'=yyyyyyyyyyxxxxxxxxxx
将前10位和后10位用W1和W2表示,W1=110110yyyyyyyyyy,W2=110111xxxxxxxxxx,则 W1 = D800~DBFF,W2 = DC00~DFFF。
例如,U+12345表示为 D8 08 DF 45(UTF-16BE),或者08 D8 45 DF(UTF-16LE)。
但是由于这种算法的存在,造成UCS-2中的 U+D800~U+DFFF 变成了无定义的字符。
Unicode(hex.) ║ UTF-8 (binary)
000000 - 00007F ║ 0xxxxxxx
000080 - 0007FF ║ 110xxxxx 10xxxxxx
000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
可见,ASCII字符(U+0000~U+007F)部分完全使用一个字节,避免了存储空间的浪费。 而且UTF-8不再需要BOM字节。
另外,从上表中可以看出,单字节编码的第一字节为[00-7F],双字节编码的第一字节为[C0-DF], 三字节编码的第一字节为[E0-EF],四字节编码的第一字节为[F0-F7],多字节的非第一字节为[80-BF]。这样只要看到第一个字节的范围就可以知道编码的字节数,而且根据单个字节就能判断其是否为第一字节。 这样也可以大大简化算法。
5.3 举例说明一下:
‘一’字的Unicode码位是U+4e00,UTF-16LE流编码为 00 4E ,
二进制为 0100 1110 0000 0000 转UTF-8后为 11100100 10111000 10000000 即 UTF-8流编码为 E4 B8 80
'a'的Unicode码位是U+0061,UTF-16LE流编码为 61 00,UTF-8流编码为 61
5.4 UTF-32 ,UTF-32其实就是完全对应于UCS-4,同样有BE LE之分,由于太占空间,故而基本无人问津。
6、说了这么多,其实都是抽象的东东,不明白还是不明白,明白了也不知道明白了什么。下面说点具体点的。
我们使用UE来打开我们的*txt文件,这样我们可以方便在文本和二进制之间切换浏览。
我们为1.txt写入字符串 "a啊",用UE打开后可以看到“a啊”字样,切换到二进制可以看到流 61 B0 A1,这时1.txt的编码方式为ASCII编码,大于128的部分则是GBK编码。
我们为1.txt写入流 FF FE 61 00 4A 55,用UE打开后同样可以看到“a啊”字样,切换到二进制可以看到流 FF FE 61 00 4A 55 ,这时1.txt的编码方式为UTF-16LE。
我们为1.txt写入流 FF FE 61 B0 A1 ,用UE打开后发现是乱码,因为B061对应的并非啊字,也并非任何汉字,所以显示的东西根本无法预估。
在Wince中CString对应的字符串,他是一个UTF-16编码的字符串,当我们使用DrawText显示一个字符串时,实际上也是需要该字符串时UTF-16编码的字符串。
当我们用char buf[]="我是什么编码?";申请一个字符串时,buf中存储的是ASCII的编码格式,该字符串的流为 CE D2 CA C7 CA B2 C3 B4 B1 E0 C2 EB A3 BF 00 。
而当我们用wchar wbuf[] = _T("我是什么编码?");申请一个宽字符字符串时,wbuf中存储的是UTF-16LE的编码格式。printf("%x",wbuf[0]);打印的值为6211。
这时候有一种迷惑,是不是char的字符串都是ASCII的,wchar的字符串都是UTF-16的,答案是否定的,char字符串仅仅能说明是单字节的字符串,字符串的每个元素都是一个字节的,wchar字符串仅仅能说明其是双字节的字符串,字符串的每个元素都是两个字节的。
当我们用memcpy(buf,wbuf,sizeof(wbuf)*sizeof(wchar));将wbuf拷贝到buf中时,此时buf字符串中的字符编码依然是UTF-16的。只不过printf("%x",buf[0]);打印值为11了。
这东西果然不是那么容易说清楚的,自己慢慢理解吧。
不管你理不理解,反正我是理解了。