阅读该文章之前,请阅读以下两篇文章,了解GBK编码和Unicode编码:
GBK编码的理解_sgmcy的博客-CSDN博客
Unicode编码的理解_sgmcy的博客-CSDN博客
UTF的意思是:Unicode Transformation Format 。也就是Unicode 转换格式。可见,UTF编码与Unicode关系是密不可分的。
之前介绍Unicode的时候,介绍Unicode是采用24bit的数据,对全世界所有的字符进行编码。其中,第一个字节是面“page”,第二个和第三个字节是具体的“码”。
地球上,大概有6900多种语言,这么多种语言的文字,大部分都放在了第一个平面立面。包括英文、中文等。其他的生僻字,用的少的,放在了其他的平面内,如平面1
Unicode 是唯一编码表。 这个唯一编码表,确定了每个字符对应的Unicode的“码”值。
但是,Unicode仅仅是确定了一个行业标准,并没有规定它的实现方式——即没有规定它在内存中是如何存储的。
Unicode是采样24bit来存储各个字符的“码”值的,但是,我们知道,计算机内部,最小的存储单元是1个字节(char类型),或者存储类型是2个字节(short 类型)或者是4个字节(int类型)或者是8个字节(double类型)
计算机底层存储没有3个字节的存储变量,即没有一个变量是用来存储24bit的数据的。
这时候,有人会说,既然没有3个字节的存储变量,那么我们就直接用4字节的int类型变量来存储不就可以了嘛!
用4字节来存储,会有两个问题:
第一个问题,是浪费存储空间。
第二个问题,就是计算机底层实现有大小端的问题。
对于第一个问题,以前计算机存储空间都是十分紧缺的,我之前上大学的时候,我们老师就给我们讲,说当时他的领导,给了他30多万的经费,让他买个海量存储器回来,结果这个所谓的海量存储器,就10M左右。
当然,现在的半导体制造技术成熟了,一个小小的U盘都能制作达到128G。
但是,有些国家就不服了,比如美国。美国心想,我ASCII码多牛逼,只需要1个字节就可以存储的玩意,用了你所谓的Unicode码,一下子内存空间占用扩展了4倍,我不愿意。
对,凭什么一个字节就可以存储的东西,需要使用4倍的存储空间存储?
第二个问题,就是大小端的问题。具体的大小端的概念,请百度查询。
比如,现在不考虑存储空间的问题,就用32bit 4字节的东西来存放24bit的Unicode码。假设一个字符的Unicode的编码是0x00 01 AA BB ,
存储到第一种计算机里面的时候,它的存储格式是0x00 01 AA BB
存储到第二种计算机里面的时候,它的存储格式是0xBB AA 01 00
在解析的时候,如果不考虑这个大小端存储的问题 ,那么读取内存中这个数值再解码成Unicode码,就会出现出错。
总之:Unicode就是制定了标准协议,但是没有规定这个标准协议是如何实现的,没有规定这个Unicode码在内存中具体怎么存储的。
所以,这时候,就出现了UTF :Unicode Transformation Format
也就是说,UTF是Unicode的一种实现方式。它实现了一种转换方式,即,把用户存储在内存中的数值,读取出来,再通过一系列的转换规则,转换成Unicode的数值,这样就可以得到对应的字符了。
UTF有好多种实现方式。
比如UTF-32就是采用32bit来实现存储24bit的Unicode码值。
当然还有UTF-16,是采用16bit来存储
当然还有UTF-8采用8bit来存储。
当然还有UTF-7 ,UTF-7.5,即采用7个bit或者7.5个bit来实现Unicode的存储。
当然,使用最广泛的,还是UTF-8
例子:UTF-32就是采用32bit来存储Unicode的码值的。
假设,一个Unicode的码值是0x00 AB CD,那么存储在内存中的码值,就是0x00 00 AB CD 或者0xCD AB 00 00 。不管是大小端,总之,程序能判断电脑系统是采用大端存储还是小端存储,判断出了是大端小端存储,再把UTF-32转换成Unicode的值,就可以得到对应的字符了。
UTF-16也是类似。大部分的Unicode的码值都是在第一个平面内部的,也就是处在的范围是在0x0000~0xFFFF之间。所以UTF-16这个标准就会判断Unicode的码值,如果在65535范围内,就采用两个字节存储,如果是在第一个平面以外的字符,就采用4个字节存储。
当然,不管采用两个字节存储还是采用4个字节存储,也还是会面临大小端的存储问题。(虽然两个字节的存储,已经节约了2个字节了。)
UTF-8是按照8bit来存储。
对于Unicode的数值在0~127范围内的,那就是标准的ASCII码值,就直接采用一个字节存储。如果是在128~2047范围内,就采用两个字节存储。。。。。如上图所示。
当然,UTF-8在转换Unicode的码值时,还会根据Unicode的码值所处的范围,把最高字节设置为0,或者设置为11,设置为111,设置为10.。。。。
这个其实就是一种转换规则而已。程序上是可以实现的。
而对于我们普通程序员用户而言,可以不需要理解这个转换规则,我们只要知道的是,在写代码的时候,我们告诉程序,我们支持的是UTF-8字符集,底层就可以实现这种UTF-8数值——Unicode码值的转换,就可以自动帮我们得到对应的想要的字符。
由于UTF-8是按照一个字节来存储的,所以,也就不存在所谓的大小端的问题。
这也是UTF-8使用最为广泛的原因之一。
当然,历史上还有UTF-7和UTF-7.5,就不表述了。
所以,一般在写python代码的时候,为了增加中文字符集的支持,一般都要声明一下,python采用utf-8编码,这样就不会出现乱码了。