只要涉及编程工作,编码是永远绕不开的问题。只有彻底理解编码,遇到编码问题才知道问题的根源在哪里,并找到对应的解决办法。花一点时间去彻底消化并理解他,长远来看,对以后工作效率的提升是非常值得的。下面是我对编码的一些总结和理解,有不对之处还望指正。
1. 什么是编码?为什么会有编码(可参考这里)?
从根本上来说,计算机只能处理 0
和1
,也就是说只能处理由 0 和 1 组成的一串串数字。让人直接用 01
数字给计算机下达指令或传输字符显然是很困难的;但其实,早期计算机刚兴起时,那时的程序员就是写好 01
代码传给机器执行的。
人用自然语言沟通,而计算机只处理 01
代码。为了人与计算机更友好的交互,有人想了办法,将自然语言的每个字符都用一串 01
数字串来表示,也就是对每个字符用 01
串来编码,比如 A 用 0100 0001
来表示,这样就可以制定一个字符与 01
数字串的对应表,也即编码表,人只需要输入自然语言(如:Hello),计算机通过查编码表就可以转换为 01
串,这样机器就可以理解并处理了。
2. 为什么会有多种编码?
众所周知,世界上第一台计算机是在美国诞生的,因此,第一种编码自然是解决英文和 01
串的对应关系,这就促生了 ASCII 编码的出台,ASCII 编码表用一个字节(8 位)包含大小字母、数字、标点符号以及控制字符。
随着计算机的普及,世界上开始有越来越多的国家使用计算机,然而,每个国家都有自己的一套语言,加起来的字符成千上万。而 ASCII 最多只能编码 256 个字符,已经不能满足这么多的需求。于是,各国就制定了自己国家的编码表。比如中国就制定了 GB2312、GBK、GB18030 等编码规范。每个国家都有自己的一套编码,这就导致同一串 01
数字可能代表两个国家不同的字符,或者同一个字符 A,在两个国家分别用不同的 01
数字串表示。这就导致不同编码的系统互相传输信息时无法正确识别,比如中国用 GBK 编码的字符传输给美国的服务器,而美国服务器只有 ASCII 编码表,这就很糟糕。
于是,Unicode 编码出现了。Unicode 编码将世界上各个国家所有的字符全部收录进去,每个字符都用唯一的 01
字符串表示,这样的话就做到了全世界所有字符的统一编码,各个国家传输信息都用 Unicode 编码,根据 Unicode 编码表就可以识别了。
常用的编码
- ASCII:1 个字节,实际使用 7 位,第 8 位保留,表示英文字符
- ISO-8859-1:1 个字节,ASCII 的升级版,在 ASCII 的基础上添加了欧洲国家的字符,不能表示中文, 常用于 Java 服务的网络传输
- GB2312:2 个字节,收录 6763 个汉字,只能表示简体字
- GBK:GB2312 的升级版,1 个或 2 个字节,收录 21003 个汉字,可以表示简体字和繁体字
-
Unicode(即 UCS):原始的 Unicode 是定长的 4 个字节,比如字母 A,用 Unicode 表示需要 4 个字节,比 ASCII 多了 3 个字节,由于这样做太浪费空间,因此经过优化制定了变长表示字符的 UTF 编码。
- UTF-8:用 1 - 6 个字节表示所有字符,此处使用变长字节表示,优先使用短字节。
- UTF-16:用 2、4 个字节表示所有字符,此处使用变长字节表示,优先使用短字节。
- UTF-32:用 4 个字节表示。
编码和解码
- 编码:将字符转为二进制(或 unicode)。
- 解码:将二进制(或 unicode)转为字符。
不同编码转换
首先应该知道,内存统一用 Unicode 编码,编码转换都统一转到 Unicode,再从 Unicode 转为其他编码。比如 ISO-8859- 1 和 UTF- 8 之间转换,流程如下:
- 先知道字符 ”u” 是用 ISO-8859- 1 编码
- 获取字符 ”u” 的二进制,java 可通过 getByte(“ISO-8859-1”)正确解析出该字符的二进制,也可以用十六进制表示,比如
AF8U
- 根据某种规则,将 ISO-8859- 1 的
AF8U
转化 Unicode, 再转化为 UTF- 8 的4E2D
java 编译的编码问题(可参考这里):
- JDK 先检查源文件的编码类型,如果没有指定编码类型则用系统的编码读取源文件,如果指定了编码类型则用指定的编码类型打开源文件。
- 打开源文件后,在内存中编译为.class 文件,此时,.class 文件是 Unicode 编码
- 将内存中的.class 文件存入硬盘,此时,.class 文件仍然是 Unicode 编码
浏览器编码问题
- 看浏览器使用的是什么编码,则显示的页面就是使用什么编码。
- 发送 post 请求时,看浏览器用的什么编码,则发送过去的数据就是用什么编码
- 一般可以查看 headers 里 ContentType,charset 是什么编码
服务器编码问题(可参考这里)
- 服务器收到请求后,经过正确的编码规则解析,服务器才可以正常识别
- 服务器发送请求时,经过正确的编码规则编码,浏览器才可以正常显示。浏览器也可以自定义编码去解析
记住: 不管是爬虫获取的,浏览器收到的还是从本地硬盘读取的,都是二进制,选择正确的编码类型,才能把二进制或者说 01 序列解析为正确的字符。也就是用何种方式解析 01 数字。