zoukankan      html  css  js  c++  java
  • 由谈退格键的实现来学习字符编码

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

      这次在公司做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);
    }

      

  • 相关阅读:
    [读书笔记]子查询
    [读书笔记]SQL约束
    [转载]NoSQL数据库的基础知识
    利用C#实现对excel的写操作
    [转载]SQL Server内核架构剖析
    利用花生壳和IIS发布网页过程
    [读书笔记]ASP.NET的URL路由引擎
    [翻译]比较ADO.NET中的不同数据访问技术(Performance Comparison:Data Access Techniques)
    [正则表达式]基础知识总结
    CF623E Transforming Sequence
  • 原文地址:https://www.cnblogs.com/danshui/p/2559158.html
Copyright © 2011-2022 走看看