字符集和字符编码

热度 3已有 1051 次阅读2012-8-10 17:34 |个人分类:技术| 字符集, 字符编码

Unicode

编码方式

Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换。

 

Unicode国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。UTF-8UTF-16UTF-32都是将数字转换到程序数据的编码方案。

 

通用字符集(Universal Character SetUCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-44个字节编码。

 

UCS-4根据最高位为0的最高字节分成2^7=128group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256 row),每行有256个码位(cell)。group 0的平面0被称作BMPBasic Multilingual Plane)。UCS-4BMP去掉前面的两个零字节就得到了UCS-2每个平面有2^16=65536个码位。Unicode计划使用了17个平面,一共有17*65536=1114112个码位。在Unicode 5.0.0版本中,已定义的码位只有238605个,分布在平面0、平面1、平面2、平面14、平面15、平面16。其中平面15和平面16上只是定义了两个各占65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFFD0x100000-0x10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为PUA

如前所述在Unicode 5.0.0版本中,238605-65534*2-6400-2408=99089余下的99089个已定义码位分布在平面0、平面1、平面2和平面14上,它们对应着Unicode目前定义的99089个字符,其中包括71226个汉字。

实现方式

Unicode中:汉字对应的数字是23383。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8UTF-16UTF-32UTF“UCS Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,汉字对应的数字是0x6c490x5b57,而编码的程序数据是:

 

BYTE data_utf8[] = {0xE6, 0xB1======-, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8编码

  WORD data_utf16[] = {0x6c49, 0x5b57}; // UTF-16编码

DWORD data_utf32[] = {0x6c49, 0x5b57}; // UTF-32编码

汉字UTF-8编码需要6个字节。汉字UTF-16编码需要两个WORD,大小是4个字节。汉字UTF-32编码需要两个DWORD,大小是8个字节。根据字节序的不同,UTF-16可以被实现为UTF-16LEUTF-16BEUTF-32可以被实现为UTF-32LEUTF-32BE。、

UTF-8

UTF-8的特点是对不同范围的字符使用不同长度的编码。

Unicode编码(16进制)  UTF-8 字节流(二进制)

000000 - 00007F  0xxxxxxx                                   //平面0

  000080 - 0007FF  110xxxxx 10xxxxxx                          //平面0

  000800 - 00FFFF  1110xxxx 10xxxxxx 10xxxxxx                 //平面0

010000 - 10FFFF  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx        //平面1~16

 

Unicode的最大码位0x10FFFF也只有21位。

1字的Unicode编码是0x6C490x6C490x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89

2Unicode编码0x20C300x010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0

 

UTF16

如果U<0x10000UUTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。

如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxxUUTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx

为什么U'可以被写成20个二进制位?Unicode的最大码位是0x10ffff,减去0x10000后,U'的最大值是0xfffff,所以肯定可以用20二进制位表示。

例如:Unicode编码0x20C30,减去0x10000后,得到0x10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30

 

为了将一个WORDUTF-16编码与两个WORDUTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate):

D800DB7F  High Surrogates  高位替代

  DB80DBFF  High Private Use Surrogates  高位专用替代

  DC00DFFF  Low Surrogates  低位替代

高位替代就是指这个范围的码位是两个WORDUTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORDUTF-16编码的第二个WORD

如果一个字符的UTF-16编码的第一个WORD0xDB800xDBFF之间,那么它的Unicode编码在什么范围内?我们知道第二个WORD的取值范围是0xDC00-0xDFFF,所以这个字符的UTF-16编码范围应该是0xDB80 0xDC000xDBFF 0xDFFF。我们将这个范围写成二进制:

  1101101110000000 11011100 00000000 - 1101101111111111 1101111111111111

  按照编码的相反步骤,取出高低WORD的后10位,并拼在一起,得到

1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111

0xe0000-0xfffff,按照编码的相反步骤再加上0x10000,得到0xf0000-0x10ffff。这就是UTF-16编码的第一个WORD0xdb800xdbff之间的Unicode编码范围,即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区,所以0xDB800xDBFF之间的保留码位被称作高位专用替代。

字节序

  根据字节序的不同,UTF-16可以被实现为UTF-16LEUTF-16BEUTF-32可以被实现为UTF-32LEUTF-32BE。例如:

  Unicode编码  UTF-16LE  UTF-16BE  UTF32-LE  UTF32-BE

  0x006C49  49 6C  6C 49  49 6C 00 00  00 00 6C 49

0x020C30  43 D8 30 DC  D8 43 DC 30  30 0C 02 00  00 02 0C 30

 

 

Unicode环境

在非 Unicode 环境下,由于不同国家和地区采用的字符集不一致,很可能出现无法正常显示所有字符的情况。微软公司使用了代码页转换表的技术来过渡性的部分解决这一问题,即通过指定的转换表将非 Unicode 的字符编码转换为同一字符对应的系统内部使用的 Unicode 编码。XML及其子集HTML采用UTF-8作为标准字集,理论上我们可以在各种支持XML标准的浏览器上显示任何地区文字的网页,只要电脑本身安装有合适的字体即可。

 

综述

对于中文而言,Unicode 16编码里面已经包含了GB18030里面的所有汉字(27484个字),目前Unicode标准准备把康熙字典的所有汉字放入到Unicode 32bit编码中。

Unicode扩展自ASCII字元集。在严格的ASCII中,每个字元用7位元表示,或者电脑上普遍使用的每字元有8位元宽;而Unicode使用全16位元字元集。这使得Unicode能够表示世界上所有的书写语言中可能用於电脑通讯的字元、象形文字和其他符号。

先天即被ANSI束缚的C程序设计语言通过对宽字元集的支持来支持Unicode

基本上,计算机只是处理数字。它们指定一个数字,来储存字母或其他字符。两种编码可能使用相同的数字代表两个不同的字符,或使用不同的数字代表相同的字符。任何一台特定的计算机(特别是服务器)都需要支持许多不同的编码,但是,不论什么时候数据通过不同的编码或平台之间,那些数据总会有损坏的危险。

 

 

ANSI

       unicodeansi都是字符代码的一种表示形式。

  为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 字节来表示 1 个字符。

不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。

不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。

 

 

字符集

字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。

 

字符编码

对字母和符号进行编码的二进制代码称为字符代码(Character Code)。计算机中常用的字符编码有ASCII码(美国标准信息交换码)和EBCDIC码(扩展的BCD交换码)。

GB2312编码

GB 2312是一个简体中文字符集,由6763个常用汉字和682个全角的非汉字字符组成。首先构造一个9494列的方阵,对每一行称为一个,每一列称为一个 如第一个汉字出现在第16区的第1位上,其区位码为1601。由于区码和位码的取值范围都是在194之间,这样的范围同西文的存储表示冲突。例如汉字GB2312中的区位码为7174,其两字节表示形式为7174;而两个西文字符‘GJ’的存储码也是71,74。这种冲突将导致在解释编码时到底表示的是一个汉字还是两个西文字符将无法判断。

GB2312编码用两个字节(82进制)表示一个汉字,所以理论上最多可以表示256×256=65536个汉字。但这种编码方式也仅仅在中国行得通,如果您的网页使用的GB2312编码,那么很多外国人在浏览你的网页时就可能无法正常显示,因为其浏览器不支持GB2312编码。

Unicode编码

Unicode固然统一了编码方式,但是它的效率不高,比如UCS-4(Unicode的标准之一)规定用4个字节存储一个符号,那么每个英文字母前都必然有三个字节是0,这对存储和传输来说都很耗资源。

UTF-8编码

为了提高Unicode的编码效率,于是就出现了UTF-8编码。UTF-8可以根据不同的符号自动选择编码的长短。比如英文字母可以只用1个字节就够了。字的Unicode编码是U+00006C49,然后把U+00006C49通过UTF-8编码器进行编码,最后输出的UTF-8编码是E6B189

 

wchar_t

wchar_tC/C++的字符数据类型,是一种扩展的字符存储方式,wchar_t类型主要用在国际化程序的实现中,但它不等同于unicode编码。unicode编码的字符一般以wchar_t类型存储。char8位字符类型,最多只能包含256种字符,许多外文字符集所含的字符数目超过256个,char型无法表示。

  wchar_t数据类型一般为16位或32位,但不同的CC++库有不同的规定,如GNU Libc规定wchar_t32[1],总之,wchar_t所能表示的字符数远超char型。

使用:

#include <iostream>

#include <stdlib>

using namespace std;

void main()

{

  locale loc( "chs" );//定义“区域设置”为中文方式

  wcout.imbue( loc );//载入中文字符输入方式

  wchar_t str[]=L"中国";//定义宽字符数组,注意L是大写

  wcout<<str<<endl;//显示宽字符数组,下同

  wprintf(str);

  system("pause");

}

转化:(wchar_t的高位字节存入c_cn[0],低位存入c_cn[1]

#include <iostream>

#include <cstdlib>

using namespace std;

char *W2C(wchar_t , char c_cn[2]);

void main()

{

       wchar_t w_cn = '';

       char c_cn[2] = {'0'};

       printf(c_cn);

       printf("\n");

       W2C(w_cn, c_cn);

       printf(c_cn);

       system("pause");

}

char *W2C(wchar_t w_cn, char c_cn[2])

{

       c_cn[0] = w_cn >> 8;

       c_cn[1] = w_cn;

       c_cn[2] = '\0';

       return c_cn;

}

 

 


路过

鸡蛋
3

鲜花

握手

雷人

刚表态过的朋友 (3 人)

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

返回顶部