编程中常见编码介绍
摘要:本文在对各种资料整理后详细介绍各种常见编码的转换算法。
一、通用字符集(UCS)
ISO/IEC 10646-1 [ISO-10646]定义了一种多于8比特字节的字符集,称作通用字符集(UCS),它包含了世界上大多数可书写的字符系统。已定义了两种多8比特字节编码,对每一个字符采用四个8比特字节编码的称为UCS-4,对每一个字符采用两个8比特字节编码的称为UCS-2。它们仅能够对UCS的前64K字符进行编址,超出此范围的其它部分当前还没有分配编址。
二、基本多语言面(BMP)
ISO 10646 定义了一个31位的字符集。然而,在这巨大的编码空间中,迄今为止只分配了前65534个码位(0x0000 到0xFFFD)。这个UCS的16位子集称为“基本多语言面”(Basic Multilingual Plane, BMP)。
三、Unicode编码
历史上,有两个独立的,创立单一字符集的尝试。一个是国际标准化组织(ISO)的ISO 10646 项目;另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的Unicode 项目。幸运的是,1991年前后,
两个项目的参与者都认识到:世界不需要两个不同的单一字符集。它们合并双方的工作成果,并为创立一个单一编码表而协同工作。两个项目仍都存在并独立地公布各自的标准,但Unicode 协会和ISO/IEC JTC1/SC2 都同意保持Unicode 和ISO 10646 标准的码表兼容,并紧密地共同调整任何未来的扩展。Unicode 标准额外定义了许多与字符有关的语义符号学,一般而言是对于实现高质量的印刷出版系统的更好的参考。
四、UTF-8编码
UCS-2和UCS-4编码很难在许多当前的应用和协议中使用,这些应用和协议假定字符为一个8或7比特的字节。即使新的可以处理16比特字符的系统,却不能处理UCS-4数据。这种情况导致一种称为UCS转换格式(UTF)的发展,它每一种有不同的特征。UTF-8(RFC 2279),使用了8比特字节的所有位,保持全部US-ASCII取值范围的性质:US-ASCII字符用一个8比特字节编码,采用通常的US-ASCII值,因此,在此值下的任何一个8比特位字节仅仅代表一个
乱码符号有哪些US-ASCII字符,而不会为其他字符。它有如下的特性:
1)UTF-8向UCS-4,UCS-2两者中任一个进行相互转换比较容易。
2)多8比特字节序列的第一个8比特字节指明了系列中8比特字节的数目。3)8比特字节值FE和FF永远不会出现。
4)在8比特字符流中字符边界从哪里开始较容易发现。
UTF-8定义:
在UTF-8中,字符采用1到6个8比特字节的序列进行编码。仅仅一个8比特字节的一个序列中,字节的高位为0,其他的7位用于字符值编码。n(n>1)
个8比特字节的一个序列中,初始的8比特字节中高n位为1,接着一位为0,此字节余下的位包含被编码字符值的位。接着的所有8比特字节的最高位为1,接着下一位为0,余下每个字节6位包含被编码字符的位。
下表总结了这些不同的8比特字节类型格式。字母x指出此位来自于进行编码的UCS-4字符值。
UCS-4范围(16进制)UTF-8 系列(二进制)
0000 0000<->0000 007F  0xxxxxxx
0000 0080<->0000 07FF  110xxxxx 10xxxxxx
0000 0800<->0000 FFFF  1110xxxx 10xxxxxx 10xxxxxx
0001 0000<->001F FFFF  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 0020 0000<->03FF FFFF  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000<->7FFF FFFF  1111110x 10xxxxxx (10xxxxxx)
从UCS-4 到UTF-8编码规则如下:
1)从字符值和上表第一列中决定需要的8比特字节数目。着重指出的是上表中的行是相互排斥的,也就是说,对于一个给定的UCS-4字符,仅仅有一个有效
的编码。
2)按照上表中第二列每行那样准备8比特字节的高位。
3)将UCS字符值的位,从低位起填充在标记为x地方。从UTF8序列中最后一个字节填起,然后剩下的字符值依次放到前一个字节中,如此重复,直到所有标记位x的位都进行了填充。
这里我们仅仅实现Unicode到UTF8的转换,Unicode都是两个字节,定义为: typedef usigned short WCHAR
// 输出的UTF8编码至多是3个字节。
int UnicodeToUTF8(WCHAR ucs2, unsigned char *buffer)
{
memset(buffer, 0, 4);
if ((0x0000 <= ucs2) && (ucs2 <= 0x007f))  // one char of UTF8
{
buffer[0] = (char)ucs2;
return 1;
}
if ((0x0080 <= ucs2) && (ucs2 <= 0x07ff))  // two char of UTF8
{
buffer[1] = 0x80 | char(ucs2 & 0x003f);
buffer[0] = 0xc0 | char((ucs2 >> 6) & 0x001f);
return 2;
}
if ((0x0800 <= ucs2) && (ucs2 <= 0xffff))  // three char of UTF8
{
buffer[2] = 0x80 | char(ucs2 & 0x003f);
buffer[1] = 0x80 | char((ucs2 >> 6) & 0x003f);
buffer[0] = 0xe0 | char((ucs2 >> 12) & 0x001f);
return 3;
}
return 0;
}
理论上,简单的通过用2个0值的8比特字节来扩展每个UCS-2字符,则从UCS-2到UTF-8编码的算法可以从上面得到。然而,从D800到DFFF间的UCS-2值对(用Unicode说法是代理对),实际上是通过UTF-16来进行UCS-4字符转换,因此需要特别对待:UTF-16转换必须未完成,先转换到于UCS-4字符,然后按照上面过程进行转换。
从UTF-8到UCS-4解码过程如下:
1)初始化UCS-4字符4个8比特字节的所有位为0。
2)根据序列中8比特字节数和上表中第二列(标记为x位)来决定哪些位编码
用于字符值。
3)从编码序列分配位到UCS-4字符。首先从序列最后一个8比特字节的最低位开始,接着向左进行,直到所有标记为x的位完成。如果UTF-8序列长度不大
于3个8比特字节,解码过程可以直接赋予UCS-2。
WCHAR UTF8ToUnicode(unsigned char *buffer)
{
WCHAR temp = 0;
if (buffer[0] < 0x80)
// one char of UTF8
{
temp = buffer[0];
}
if ((0xc0 <= buffer[0]) && (buffer[0] < 0xe0))      // two char of UTF8
{
temp = buffer[0] & 0x1f;
temp = temp << 6;
temp = temp | (buffer[1] & 0x3f);
}
if ((0xe0 <= buffer[0]) && (buffer[0] < 0xf0))      // three char of UTF8
{
temp = buffer[0] & 0x0f;
temp = temp << 6;
temp = temp | (buffer[1] & 0x3f);
temp = temp << 6;
temp = temp | (buffer[2] & 0x3f);
}
if ((0x80 <= buffer[0]) && (buffer[0] < 0xc0))      // not the first byte of UTF8 character
return 0xfeff;
// 0xfeff will never appear in usual
return temp;
// more than 3-bytes return 0
}
注意:上面解码算法的实际实现应该进行安全保护,以便处理解码无效的系列。
例如:实现可能(错误)解码无效的UTF-8系列0xC0 0x80为字符U+0000,它
可能导致安全问题或其他问题(比如把0当作数组结束标志)。更详细的算法和公式可以在[FSS_UTF],[UNICODE] 或[ISO-10646]附录R中到。
五、UTF-7编码
UTF-7:A Mail-Safe Transformation Format of Unicode(RFC1642)。这是一种使
用7 位ASCII 码对Unicode 码进行转换的编码。它的设计目的仍然是为了在
只能传递7 为编码的邮件网关中传递信息。UTF-7 对英语字母、数字和常见
符号直接显示,而对其他符号用修正的Base64 编码。符号+ 和- 号控制编
码过程的开始和暂停。所以乱码中如果夹有英文单词,并且相伴有+ 号和- 号,这就有可能是UTF-7 编码。
协议中定义的转换规则:
1)集合D中的Unicode字符可以直接的编码为ASCII的等值字节。集合O中的
字符可以有有选择的的直接编码为ASCII的等值字节,但要记得其中的很多的字
符在报头字段是不合法的,或者不能正确的穿过邮件网关。
2)通过在前面加上转换字符"+",任何一个Unicode序列都可以使用集合B(更
改过的base64)中的字符编码。"+"意味着后面的字节将被作为更改过的BASE64字母表中的元素解析,直到遇到一个不是字母表中的字符为止。这些字符中会包
含控制字符,比如回车和换行;因此,一个Unicode转换序列总是在一行上结束。
注释:有两个特殊的情形:"+-"表示''+'',"+ …… --"表示有一个真正的''-''字符
出现了。多数情况是没有''-''标记结束。
3)空格、tab、回车和换行字符可以直接使用ASCII等价字节表示。
那么我们就可以定义算法了,我们先定义字符集的相关数组:
typedef unsigned char byte
// 64 characters for base64 coding
byte base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567 89+/";
// 8 characters are safe just as base64 characters for MAIL gates byte safeChars[] = "''(),-.:?";
// 4 characters all means space
byte spaceChars[] = " \t\n\r";

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。