谈谈乱码的根源:内存编码和文件编码
注意事项
为了便于说明问题,本文可能在某些地方描述的不完全准确,后面有读者进行了补充和修订,务必把补充和修订部分仔细阅读。
1、什么是字元?
本质上说,计算机只能存储字节,即8比特的值,如果是无符号数,那么取值范围从0x00到OxFF,每个字符必须都以某种形式的字节表示。在计算机技术的早期,研究者们设计的编码机制是使用一个特定字节表示某个特定的字符。例如,使用ASCI编码,就用0x41表示A,用0x42表示B,依此类推。在西欧,通常使用的是Latin-I编码,其前127个字符与7比特ASCII相同,其余部分则用于重音字符与欧洲人需要的其他符号。在计算机科学的发展中,研究者还设计了很多种其他编码方式,其中的大部分仍然在使用中。
遗憾的是,存在太多的编码方式会带来很多不便,在编写国际化软件时更是如此。一个几乎被最广泛采纳的标准是Unicode编码,在这种编码中, Unicode 为每个字符分配一个整数,即字元,就像早期的编码方式一样,但是Unicode不局限于使用一个字节表示每个字符,因而有能力使用这种编码方式表示每种语言中的每个字符。并且,作为对其表示能力的一种增强,其中的前127个Unicode字符与7比特ASCI表示的前127个字符是相同的。
2、Unicode是如何存储的?
Unicode是如何存储的?当前,定义了超过100万个Unicode字符,因此,即便使用有符号数字,一个32位整数也足以存放任何Unicode字元,因此,最简单的用于存储Unicode字符的方式是使用32位整数序列,每个整数代表一个字符。在内存中,这是非常便利的,因为我们可以设计一个32位整数数组,数组中的每个元素与某个字符有一对一的对应关系。但对文件或通过网络连接发送的文本而言,尤其是在文本几乎都是7比特ASCII时,每个整数的4个字节中最多有3个字节会是0x00。为避免这样的浪费,Unicode自身有几种表示方式。
2.1 在内存中的存储
在内存中,Unicode 通常以UCS-2格式(实质上是16比特的无符号整数)表示前65535个字元,或者以USC-4格式(32位整数)表示所有的字元。
2.2 文件存储的形式、网络传输的形式
对存放在文件中或通过网络连接传送的数据,情况会更加复杂。如果使用了Unicode,那么字元可以使用UTF-8进行编码这种编码中,对前127个字元,每个字符使用一个字节表示,对其他字元,则使用两个或更多的字节数来表示每个字符。对英文文本而言,UTF-8是非常紧凑的,如果只使用了7比特字符,则UTF-8文件与ASCII文件实质上是一样的。另一种常见的编码方式是UTF-16编码,这种编码方式中,对大多数字符使用两个字节表示,对其他的一些字符则使用4个字节表示。对某些亚洲语言,这种编码方式比UTF-8更紧凑,但与UTF-8不同的是,UTF-16 文本应该以一个字节顺序标记开始,以便用于读取该文本的代码可以判定字节对是big: endian还是lttle endian.此外,所有旧的编码格式,比如GB2312、ISO-8859-5. Latin-1 等, 实际上都在常规的使用中。
3、字符串在内存中存储的形式是什么?
字符串是以Unicode编码存储在内存的,存储的是字元,如下所示:
所以,str = 'u4e2d',这样的赋值形式才是最合理的,但是这样对计算机合理了,但是对人不合理,那只能是:str = '中'。
4、字符串的编码和解码是什么?
如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。这个时候需要进行编码,编码就是将Unicode通过某种格式变成字节序列:
'中'.encode('utf-8'),经过utf-8编码之后,变成的字节序列为:
5、UTF-8 编码
此处内容请参考网络即可。
6、热心读者的补充和修订:
Unicode只是字符集 是无法落地的 存在内存和硬盘里的必须是某一种编码,Java内部存储是utf16。
ucs-2这种说法已经废弃了,统一称为utf-16。
.。。老是搞不明白编码,希望大神能发篇文章
好的,我把这篇文章更新一下。