淡水求咸

博客园已经停止更新,请移步 http://lovecjh.com/

导航

由谈退格键的实现来学习字符编码

  我曾以为老师的话是真的,我曾以为老师会为自己说出的话负责,但事实证明很多时候是照本宣科。

  这次在公司做Fcitx输入法时,想到退格删除的字节数的不同,即退格键一按到底删除的是一个字节还是两个字节或者多个。在测试中我发现,中文汉字占了三个字节,竟然占了三个字节,老师不是经常告诉我们汉字是占两个字节,但现在怎么占了三个字节,是老师的错还是程序的错,亦或本都没错,只是没有探其根本罢了。

  说汉字占两个字节是从以Unicode的编码方式UCS-2来说的,但实际占三个字节,是从Unicode的实现方式UTF-8来说的。

  先介绍下Unicode字符集吧。

  什么是字符?什么是字符集?

  字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。

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

  多年来,许多人一直将文本串作为一系列单字节字符来进行编码,并在结尾处放一个零。对于我们来说,很习惯调用strlen函数来返回以0结尾的单字节字符数组中字符数目。这是我们平时所说的ASCII字符,即单字节字符,一个字符占一个字节,即八位,所以它提供的符号最多不能超过1+28-1=256个字符,但明显单字节是根本不够用的,为此出现了双字节字符集(DBCS)。

  Unicode的编码和实现。

  大概来说,Unicode编码系统可分为编码方式和实现方式两个层次。

  编码方式

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

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

  实现方式

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

  BYTE date_utf8[]={0xE6,0xB1,0x89,0xE5,0xAD,0x97};

  可见,一个汉字以UTF-8编码是占三个字节。

  UTF-8

  UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:

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

  000000-00007F        0xxxxxxx

  000080-0007FF        110xxxxx  10xxxxxx        

  000800-00FFFF        1110xxxx  10xxxxxx  10xxxxxx

  010000-10FFFF        11110xxx  10xxxxxx  10xxxxxx  10xxxxxx

  UTF-8的特点是对不同范围的字符使用不同长度的编码,所以UTF-8兼容ASCII编码。UTF-8编码的最大长度是4个字节。从上表可以看出,4个字模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。

  从Unicode编码的UTF-8实现方式比较好理解:

  例如,汉字的“汉”字的Unicode编码是0x6C49。0x6C49是在0x800-0xFFFF之间,则对应用UTF-8来实现需要点三个字节,1110xxxx  10xxxxxx  10xxxxxx。

  0x6C49的二进制是0110 1100 0100 1001,用这个比特流依次代替模板中的x,即用0110 1100 0100 1001从低位到高位替代1110xxxx  10xxxxxx  10xxxxxx的x位,不足时用0代替。

  0110 1100 0100 1001  

  1110xxxx  10xxxxxx  10xxxxxx

  11100110  10110001  10001001

  现在谈谈针对是UTF-8编码方式而言退格键的实现。

void utf8_backspace(char *text)
{
    int n = strlen(text);
    char *last_byte = text + n - 1;

    if(!((*last_byte) & 0x80)) 
    {
        text[n-1] = '\0';
    } 
    else if(((*(last_byte-1)) & 0xc0) == 0xc0) 
    {
        text[n-2] = '\0';
        text[n-1] = '\0';
    }
    else if(((*(last_byte-2)) & 0xe0) == 0xe0) 
    {
        text[n-3] = '\0';
        text[n-2] = '\0';
        text[n-1] = '\0';
    }  
    else if(((*(last_byte-3)) & 0xf0) == 0xf0) 
    {
        text[n-4] = '\0';
        text[n-3] = '\0';
        text[n-2] = '\0';
        text[n-1] = '\0';
    }  
    else
    {       
        printf("[%s]:%d,uft8 backspace error\n",_FILE_,_LINE_);

    }
}

  UTF-8理论上最多可以是6个字节,这里只考虑4个字节,因为大部分4个字节就已经足够。

  这个实现退格键的前提是你的字符是经过UTF-8,如果是其它的,如GB2312等须先进行编码转换成UTF-8才能用。  

  

  Unicode能显示20901个汉字,范围是从\u4e00到\u9fa5,下面程序是输出Unicode编码下的所有汉字。  

 //C# code

char minHZUnicode = '\u4e00';
char maxHZUnicode = '\u9fa5';

for (char c = minHZUnicode; c <= maxHZUnicode; c++)
{
         Console.Write(c);
}

 

 

  

posted on 2012-06-23 14:56  深圳彦祖  阅读(1178)  评论(0编辑  收藏  举报