聊聊字符编码

你是否有过这样的经历,文件在别人电脑上看着好好的,可是拷贝到本身的电脑上就发现乱码了。或者做为程序员的你,打开别人的代码时,发现中文注释乱码了,而代码缺一点毛病都没有。我曾经也被这些问题困扰许久,虽然不少时候转换一下字符编码问题就解决了,可是背后的缘由却一直缕不顺。所以,想借这篇文章,尽量把这件事情给说清楚。程序员

字符编码的起源-ASCII码

计算机存储数据都是以二进制的形式进行存储的,可是二进制形式人类是没法直接解读的。所以,对于保存在计算机上的文本数据,须要一张映射表实现二进制与文字之间的相互转换。好比,能够约定0000 0001表明字母A,0000 0002表明字母B,所以当你保存一段文本ABA时,实际上计算机存储的是0000 0001 0000 0002 0000 0001。相应的,当你打开这个文件时,本质读取的二进制数据,可是编辑器会将这段二进制码查表转换成ABA编辑器

计算机问世的早期,有这样一张映射表被创建做为编解码的标准,称为ASCII码。 ide

ASCII码
如上图所示,ASCII码用7个bit位表示,范围从0~127,一共128个字符。在计算机问世的早期,主要只有英文符号,并且存储空间十分有限,所以用一个字节就可以知足需求了。可是随着计算机的快速普及,各国都有将本国文字进行输入输出的需求,可是ASCII码没法知足这样的需求,所以须要新的编码。

百花齐放-多字符集

对于拉丁语系国家而言,他们能够扩展ASCII码的第8个bit位来知足本国的编码需求,可是对于非拉丁语系的国家而言,单字符集(只用了一个字节完成编码的称为单字符集,对应的就是多个字节编码的成为多字符集)没法知足编码本国语言的需求,所以他们用多个字节来表示一个文字。例如,咱们国家就是使用的双字节字符编码,使用最为普遍的就是GB2312编码。可是GB2312只能编码简体中文,所以出现了GBK编码,GBK编码除了支持简体中文,还支持繁体中文以及日语、韩语等的编码,是大一统的编码。ui

GBK的编码规则 GBK编码使用1-2个字节进行编码,GBK编码分为不少个码页,每一个码页的范围编码的范围都是一个字节,即0x00-0xFF。GBK编码先经过第一个字节查询第一张表(以下图)。 编码

若是第一个字节的首位为0,也就是范围在0x00-0x80之间时,直接从该表中查询获得对应的字符,和传统的ASCII码查询的方式相同。例如百分号%的编码就是0x25。spa

若是第一个字节的首位为1,也就是范围在0x81-0xFF之间时,先查询该表获得第二个字节须要查询的编码页的页码号。例如汉字的编码为8144,第一个字节先查询到要查第0x81编号的页码,而后在0x81编号的页码中查询编码为0x44的对应的文字就是code

因为各国都有本身的编码,并且编码的方式还不少,规则的不统一致使,文本转化中会很麻烦,所以制定了ANSI标准,各国指定标准的多字符集编码方式,例如我国的标准编码就是GB2312。cdn

全球一统-UNICODE

因为各国都制定了本国的多字节的字符编码,致使全球范围内的字符字符编码集很是多,这会使得各国之间文字转换很是的麻烦。所以大佬们坐下约定了一个全球统一的编码,用一个编码表示全世界全部的文字的,这就是UNICODE编码。UNICODE编码是两个字节,所以能够编码256*256个字符,这基本能够知足全球字符编码的需求了。blog

咱们以汉字的为例,其Unicode编码为\u6c49\u用来标识其为Unicode编码,由0x6c0x49组成。用两个字节表示,那么就有前后顺序的问题,6c49496c两种方式都能表示,所以两种不一样顺序的编码的编码方式称为大端和小端模式字符串

Unicode实际上仍是一个比较广义的概念,在实际编码规则中常见的有UCS-2,UTF-16,UTF-8。接下来咱们分别阐述这几种编码的概念。

UCS-2与UTF-16

UCS-2是Unicode编码的标准实现,全部的字符都是按照两个字节编码。两个字节的顺序不一样就产生了USC-2大端和USC-2小端两种模式。可是UCS-2只编码了BMP字符,而UTF16则经常使用变长的方式来兼容其余字符,最短两个字符。BMP字符UTF16和UCS-2是相同的,扩展的部分则用四个字节编码。

UTF-8

终于到了咱们最熟悉的UTF-8了。Unicode编码出现后,使用拉丁文的国家发现本身吃了大亏,他们认为拉丁文本来只须要一个字节就能够编码,如今却须要两个字节,所以搞出了变长的UTF-8字符。

UTF-8的编码规则

[1] UTF-8是变长度的,长度为1-6个字节;

[2] 第一个字节的连续的二进制位值为1的个数决定了其编码的位数

[3] 若是第一个字节以0开头说明是单个字节的字符

[4] 对于非单个字节的字符,出首字节外,其他均已10开头

上面的规则用编码表示更加清晰,以下:

占用字节 首字节大小 完整表示
1字节 大于0 0xxxxxxx
2字节 大于0xc0 110xxxxx 10xxxxxx
3字节 大于0xe0 1110xxxx 10xxxxxx 10xxxxxx
4字节 大于0xf0 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 大于0xf8 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 大于0xfc 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

根据这个编码规则,就能够判断出一个字符串是由多少个字符组成,C++代码以下:

inline std::string GetHideName(const std::string& sUtf8Data) {
    std::vector<std::string> vName;
    
    std::string ch; 
    for (size_t i = 0, len = 0; i != sUtf8Data.length(); i += len)
    {
        unsigned char byte = (unsigned)sUtf8Data[i];
        if (byte >= 0xFC) // lenght 6
            len = 6;
        else if (byte >= 0xF8)
            len = 5;
        else if (byte >= 0xF0)
            len = 4;
        else if (byte >= 0xE0)
            len = 3;
        else if (byte >= 0xC0)
            len = 2;
        else
            len = 1;
        ch = sUtf8Data.substr(i, len);
        vName.push_back(ch);
    }   
    
    std::string sQxName;
    if (vName.size() <= 2)
    {
        sQxName = vName.size() > 0?(vName.front() + "*"):sUtf8Data;
    }
    else
    {
        sQxName = vName.front() + "**" + vName.back();
    }
    return sQxName;
}
复制代码

问题

[1] GBK与GB2312的区别? GBK是GB2312的超集,你能够简单这样理解,GB2312编码的是简体中文,而GBK在GB2312的基础上增长了繁体字以及日语和韩语。由于GBK是在GB2312的基础上进行扩展的,因此简体中文用这二者是相同的编码。

相关文章
相关标签/搜索