你是否有过这样的经历,文件在别人电脑上看着好好的,可是拷贝到本身的电脑上就发现乱码了。或者做为程序员的你,打开别人的代码时,发现中文注释乱码了,而代码缺一点毛病都没有。我曾经也被这些问题困扰许久,虽然不少时候转换一下字符编码问题就解决了,可是背后的缘由却一直缕不顺。所以,想借这篇文章,尽量把这件事情给说清楚。程序员
计算机存储数据都是以二进制的形式进行存储的,可是二进制形式人类是没法直接解读的。所以,对于保存在计算机上的文本数据,须要一张映射表实现二进制与文字之间的相互转换。好比,能够约定0000 0001
表明字母A,0000 0002
表明字母B,所以当你保存一段文本ABA
时,实际上计算机存储的是0000 0001 0000 0002 0000 0001
。相应的,当你打开这个文件时,本质读取的二进制数据,可是编辑器会将这段二进制码查表转换成ABA
。编辑器
计算机问世的早期,有这样一张映射表被创建做为编解码的标准,称为ASCII码。 ide
对于拉丁语系国家而言,他们能够扩展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编码是两个字节,所以能够编码256*256个字符,这基本能够知足全球字符编码的需求了。blog
咱们以汉字的汉
为例,其Unicode编码为\u6c49
,\u
用来标识其为Unicode编码,由0x6c
和0x49
组成。用两个字节表示,那么就有前后顺序的问题,6c49
和496c
两种方式都能表示,所以两种不一样顺序的编码的编码方式称为大端和小端模式字符串
Unicode实际上仍是一个比较广义的概念,在实际编码规则中常见的有UCS-2,UTF-16,UTF-8。接下来咱们分别阐述这几种编码的概念。
UCS-2是Unicode编码的标准实现,全部的字符都是按照两个字节编码。两个字节的顺序不一样就产生了USC-2大端和USC-2小端两种模式。可是UCS-2只编码了BMP字符,而UTF16则经常使用变长的方式来兼容其余字符,最短两个字符。BMP字符UTF16和UCS-2是相同的,扩展的部分则用四个字节编码。
终于到了咱们最熟悉的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的基础上进行扩展的,因此简体中文用这二者是相同的编码。