乱码,无疑是开发人员都会经历的痛。虽然每次我们都能够通过搜索引擎解决乱码的问题,但对于产生乱码的原因,还有对字符集和编码的理解,大多都是懵懵懂懂。其实编码所涉及的技术并不复杂,关键是要准确理解其概念。
字符集和编码
字符集,顾名思义,就是字符的集合,比如常见的 UNICODE、GB2312、ASCII 字符集等。字符集有两个概念:
- 使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。
- 字符的编号。每个字符对应一个编号,其实就是数字,比如 ASCII 字符集中,
a
所对应的编号就是 97,也称之为码点。
编码,是字符存储的一种实现。规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储。一个字符集可能有多套编码,比如 UNICODE 字符集就有 UTF-8、UTF-16 等编码。
它们的关系可由下图表示
几种误解
在将“字节串”转化成“UNICODE 字符串”时,比如在读取文本文件时,或者通过网络传输文本时,容易将“字节串”简单地作为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。
而实际上,在非英文的环境中,应该将“字节串”作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能“多个字节”才能得到“一个字符”。
通常,一直在英文环境下做开发的程序员们,容易有这种误解。在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维:“字符串的编码”。
当 UNICODE 被支持后,Java 中的 String 是以字符的“序号”来存储的,不是以“某种编码的字节”来存储的,因此已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时,或者,将一个“字节串”当成一个 ANSI 字符串时,才有编码的概念。不少的人都有这个误解。
第一种误解,往往是导致乱码产生的原因。第二种误解,往往导致本来容易纠正的乱码问题变得更复杂。