还在遇见乱码问题吗?也许你该了解一下编码了

67 阅读4分钟

UTF-8、GB231、GBK编码格式

UTF-8编码

使用1~4个字节对每个字符进行编码,为了性能考虑,做成了一种可变长度的编码规范。

每种字符集需要的编码字节长度

  • 128个ASCII字符只需要一个字节进行编码
  • 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节进行编码
  • 其他基本多文种平面中的字符(这包含了大部分常用字,如大部分的汉字)使用三个字节进行编码
  • 其他极少使用的Unicode辅助平面的字符使用四至六字节进行编码

GB2312编码

  • 一个小于127的字符意义与原来的相同,将两个大于127的字符连在一起,就表示一个汉字;
  • 前面的一个字节(高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE;

其中,在ASCII里本来就有个的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的“全角”字符,而原来在127号以下的那些就叫“半角”字符

GBK编码

是GB2312编码的一种扩展,不再要求低字节一定是127号之后的编码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容

示例

仅仅从文字介绍来看比较抽象,看完后也还是一头雾水,借助于nodejs,我们可以研究一下实际的案例,方便对比和理解。

纯汉字内容编码

以【联通】这两个汉字为例,参考一个两种编码格式的输出产物。

------------------GB2312编码保存的[联通]数据------------------
<Buffer c1 aa cd a8>

------------------UTF-8编码保存的[联通]数据-带BOM------------------
<Buffer ef bb bf e8 81 94 e9 80 9a>

------------------UTF-8编码保存的[联通]数据-不带BOM------------------
<Buffer e8 81 94 e9 80 9a>

可以清楚看到GB2312和UTF-8编码格式的区别:GB2312使用两个字节对汉字进行编码,而UTF-8使用三个字节对汉字进行编码

其中还着重列出了UTF-8编码的两种规范,带BOM头 VS 不带BOM头的区别,在日常中也是两种比较常见的实践。

UTF-8带BOM的编码格式和不带BOM的编码格式之间的区别:BOM格式会在头部添加ef bb bf三个字节作为标志

中英文混合内容编码

这个主要是看各个编码格式对于ASCII编码的兼容情况,以【hello 你好】内容为例,进行编码内容分析。

------------------非UTF-8编码保存的[hello 你好]数据------------------
<Buffer 68 65 6c 6c 6f 20 c4 e3 ba c3>

------------------UTF-8编码保存的[hello 你好]数据-带BOM------------------
<Buffer ef bb bf 68 65 6c 6c 6f 20 e4 bd a0 e5 a5 bd>

------------------UTF-8编码保存的[hello 你好]数据-不带BOM------------------
<Buffer 68 65 6c 6c 6f 20 e4 bd a0 e5 a5 bd>

从上面的两种编码输出结果,可以有以下结论:

  • 两种编码格式对ASCII都能兼容,前面的[hello ]两者的编码是一致的,而中文字符编码则是根据各自不同的编码规则来得
  • 如果只有ASCII字符,则两种编码是等价的,GB2312方式打开UTF-8编码的文件不会出现乱码,反之亦然

自动编码格式校验

在日常中经常进行文件的传输,在使用编辑器打开别人传输过来的文件时,如果编码格式对不上就会出现乱码的情况。考虑是不是存在一种自动转换的方法,在不同的编码格式间进行自动转换,避免出现乱码的情况。以下是自己在这方面针对UTF-8和GB2312编码格式识别的思考:

  1. 带有BOM信息的,可以根据BOM信息进行区分:前三个字节为:ef bb bf的是UTF-8编码格式
  2. 没有BOM信息的,则需要按字节进行区分:如果UTF-8中有中文字符,则可以根据1110XXXX 10XXXXXX 10XXXXXX这种格式来进行区分;如果UTF-8中有其他两个字节编码的字符则较难进行区分

经过在对用户上传文件进行分析,感觉实用性还是蛮好的。