前言:从学习编程开始,字符编码和乱码问题一直伴随在我们左右。ASCII、GBK、UTF-8、Unicode、Latin 1、ANSI、ISO-8859-1等字符编码瞬时出现在我的脑海,就像电子围绕原子核一样不停转动。但是,扪心自问,我们真的了解它们吗?它们的底层原理是什么?同样是乱码,为什么有时候是“???”,有时候是“浣犲ソ”或者“ä½ å�½”。遇到乱码,我们可以轻易的在网上找到解决方案,但是下次遇到类似甚至同样的问题,还是要去网上找答案,因为我们不知道乱码的根本原因。本文将详细介绍常见编码的底层原理以及乱码产生的原因。字符编码内容很多,很复杂,我会尽可能的把最重要的内容都书写下来,笔者如果存在错误的地方,欢迎指正
文章目录
一、字符编码基础
1. 字符编码的由来
我们使用计算机处理字符(字母、数字、标点、文字等)类型的数据,首先,需要计算机能够存储字符。但是,计算机只能识别二进制数,不可能直接把字符实体存储进计算机,因此,需要把每个字符都“映射”为一个特定的若干位的二进制数
2. 字符集和编码规则
为了方便对字符进行编码,我们给每个字符一个唯一的数字编号。
字符集:收录了需要表示的字符,并且为每一个字符分配了一个唯一的数字编号(称为:码点 Code Point,码位或码值),通常使用十进制或者十六进制数表示。
编码规则:将码点转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)
例如:ASCII字符集主要收录英文字符,[A 65 (十进制编号)]、[a 97];
ASCII编码规则是把码点转换为对应的二进制数,A 的的二进制编码是01000001
我们平时说的ASCII编码,其实是指ASCII编码规则,由于其收录字符少,编码规则简单且只有一种,字符集和编码规则就使用了同一个名字。
然而对于Unicode字符集,收录了英文字符、泰语字符、中日韩字符、蒙古语字符等等世界各地的常用字符。其收录字符多,编码规则目前主要有三种:UTF-8、UTF-16、UTF-32
要注意到,字符集和编码规则是不同的概念。
3. 编码和解码
编码依据编码规则把字符的码点转换为二进制序列
解码依据编码规则把二进制序列解析为码点,进一步说是字符
编码场景
例如:当我们使用文本编辑器,保存文件(包含所有文本文件,例如后缀为css、html、xml、java、txt),会让你选择编码,然后,文本编辑器把文件中所有的字符 编码 为二进制序列。
解码场景
例如:所有在电脑中显示字符的地方都需要解码,使用文本编辑器打开文件,浏览器展示html文档,Windows系统的命令行窗口显示程序的打印输出文本等等都需要使用默认或者你指定的编码规则去解码二进制序列。
(左边是根据UTF-8编码规则解码右边的二进制序列得到的字符)
4. 固定长度和可变长度编码
像ASCII字符集中所有字符的编码长度为一个字节,编码的字节序列长度是固定的,因此称为固定长度编码。
固定长度编码
- 优点:编码规则简单,即码点对应的二进制字节序列
- 缺点:占用存储空间大
像Unicode字符集的UTF-8常用字符编码的字节序列为一个字节到三个字节,编码的长度是不固定的,因此称为可变长编码。
可变长度编码
- 优点:占用存储空间小
- 缺点:编码规则复杂
后文会详细介绍。
二、字符编码规范
本部分介绍ASCII、ISO-8859-1及ISO-8859系列编码、Unicode字符集及UTF-8、UTF-16、UTF-32编码。如果没有特殊说明ASCII指的是字符集和对应的编码规则。
其实学习字符编码并没有想象的那么困难,最主要了解的是字符集及编码规则。另外,还有确定编码是固定长度还是可变长度,它决定了编码规则和需要解决的问题。固定长度编码,其编码的码点一般是连续的,字节数可以表示的状态大于字符集码点的个数,因此编码规则可以是码点对应的二进制数。对于所有可变长度编码,需要解决一个问题:当前的一个字节是表示一个字符还是多字节字符的某一部分。下文还会展开讲解。
1. ASCII
ASCII (读音:美 /ˈæski/ ,全称: American Standard Code for Information Interchange,中文名:美国标准信息交换码)
字符集: 主要收录英语语言字符。可以看到码点(即码值)是从0到127连续的数值,如下图所示:
编码规则: 码点对应的二进制数序列。且固定长度编码。
编码范围[0000 0000,0111 1111]
(注,固定长度编码的编码规则一般都是码点对应的二进制数序列)
例如:A 65 二进制编码序列 01000001
2. ISO-8859-1
ISO-8859-1 (别名:Latin-1)
字符集支持部分欧洲语言字符,兼容ASCII,(包括阿尔巴尼亚语、巴斯克语、布列塔尼语、加泰罗尼亚语、丹麦语、荷兰语、法罗语、弗里西语、加利西亚语、德语、格陵兰语、冰岛语、爱尔兰盖尔语、意大利语、拉丁语、卢森堡语、挪威语、葡萄牙语、里托罗曼斯语、苏格兰盖尔语、西班牙语及瑞典语。)
下图中的字符对应的码点是十六进制形式,例如A 左边4x,上边x1组合就是41,换句话说A的十六进制的码点是41,等价于十进制形式的码点65。
图中的上半部分是兼容的ASCII的码点,范围为[00,7F],是连续的
下半部分的码点,范围为[80,FF],也是连续的。浅绿色部分表示的是控制字符,不是可打印字符,因此表格中没有