Python 字符编码
所有的数据(数字,字符串等)在硬盘中存储都是二进制,读到内存中的数据本质也是二进制。
所有的数据(数字,字符串等)在网络中传输都是二进制。
因为计算机只认识 0 和 1 的二进制数字和这些数字的组合(01010010)。
但是,人类是不能以这种形式的数据来沟通信息的,所以需要制定出一组规则,来说明某些组合代表的意义 只不过,在存储或传输之前, 数据都会以某一种规则把二进制数字进行排列后在进行存储或传输
这种 规则 就叫 字符编码,字符编码可以让数据以人们能够读懂的语言形式展现出来,反过来说,就是人们需要利用一定的规则把想要表达的信息,以二进制的数据方式存放到计算机中。
主流的编码方式有:
- ASCII
- GB2312
- GBK 和 GB18030编码
- UNICODE编码
- UTF8
解释:引用自 http://www.cnblogs.com/yuanchenqi/articles/5956943.html
//ASCII
记住一句话:计算机中的所有数据,不论是文字、图片、视频、还是音频文件,本质上最终都是按照类似 01010101 的二进制存储的。
再说简单点,计算机只懂二进制数字!
所以,目的明确了:如何将我们能识别的符号唯一的与一组二进制数字对应上?于是美利坚的同志想到通过一个电平的高低状态来代指0或1,
八个电平做为一组就可以表示出
256种不同状态,每种状态就唯一对应一个字符,比如A--->00010001,而英文只有26个字符,算上一些特殊字符和数字,128个状态也够
用了;每个电平称为一个比特为,约定8个比特位构成一个字节,这样计算机就可以用127个不同字节来存储英语的文字了。这就是ASCII编码。
扩展ANSI编码
刚才说了,最开始,一个字节有八位,但是最高位没用上,默认为0;后来为了计算机也可以表示拉丁文,就将最后一位也用上了,
从128到255的字符集对应拉丁文啦。至此,一个字节就用满了!
//GB2312
计算机漂洋过海来到中国后,问题来了,计算机不认识中文,当然也没法显示中文;而且一个字节所有状态都被占满了,万恶的帝国主义亡
我之心不死啊!我党也是棒,自力更生,自己重写一张表,直接生猛地将扩展的第八位对应拉丁文全部删掉,规定一个小于127的字符的意
义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节
(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了;这种汉字方案叫做 “GB2312”。GB2312 是对 ASCII 的中文扩展。
//GBK 和 GB18030编码
但是汉字太多了,GB2312也不够用,于是规定:只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的
内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
//UNICODE编码:
很多其它国家都搞出自己的编码标准,彼此间却相互不支持。这就带来了很多问题。于是,国际标谁化组织为了统一编码:提出了标准编码准
则:UNICODE 。
UNICODE是用两个字节来表示为一个字符,它总共可以组合出65535不同的字符,这足以覆盖世界上所有符号(包括甲骨文)
//UTF-8:
unicode都一统天下了,为什么还要有一个utf8的编码呢?
大家想,对于英文世界的人们来讲,一个字节完全够了,比如要存储A,本来00010001就可以了,现在吃上了unicode的大锅饭,
得用两个字节:00000000 00010001才行,浪费太严重!
基于此,美利坚的科学家们提出了天才的想法:utf8.
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,它可以使用1~4个字节表示一个符号,根据
不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,所以是兼容ASCII编码的。
这样显著的好处是,虽然在我们内存中的数据都是unicode,但当数据要保存到磁盘或者用于网络传输时,直接使用unicode就远不如utf8省空间啦!
这也是为什么utf8是我们的推荐编码方式。
Unicode与utf8的关系:
一言以蔽之:Unicode是内存编码表示方案(是规范),而UTF是如何保存和传输Unicode的方案(是实现)这也是UTF与Unicode的区别。
这么多的编码存在的目的都是为了
节省空间和带宽
附图:


一、 python2
在 python2 中 默认使用 ASCII 编码格式把所有数据存入到硬盘的,读取到内内存时,默认也是以 ASCII 码的方式存放到内存的。
所以在文件中的字符串格式就是 ASCII 码格式;当然这样是不会支持中文字符的。
python 为了能够让用户可以看到和操作这些二进制数据,同时也是能够更易读, 在数据进行硬盘和内存之间的转换过程中,中间还有一个bytes的转换过程,这也是必须的(就是这么规定的)。
在 python2 中字符串 str 类型就是 bytes,其表现形式是:
>>> stra '\xe4\xb8\xad\xe5\x9b\xbd' # 十六进制的字节码,就是bytes 字节数据 >>>
在 python2 中 Unicode 存在的形式就是Unicode自己的形式:
>>> stra.decode('utf-8')
u'\u4e2d\u56fd'
>>>
总结:
在python2 中,又把这种形式封装了一下,其表现形式就是体现在字符串上。字符串类型的数据是有两种类型的,str 和 Unicode,str 就是 bytes。
python中的内置函数repr可以帮我们在这里显示存储内容。
>>> repr(stra)
"'\\xe4\\xb8\\xad\\xe5\\x9b\\xbd'"
>>> unicod_of_utf8=stra.decode('utf-8')
>>> repr(unicod_of_utf8)
"u'\\u4e2d\\u56fd'"
>>>
原来,str和unicode分别存的是字节数据和unicode数据;那么两种数据之间是什么关心呢?如何转换呢?这里就涉及到编码(encode)和解码(decode)了
在说具体怎么转换之前,需要先强调一下,在 python2 中默认存储在硬盘中的数据都是以 ASCII 码格式存放的。
大家知道 ASCII 码是不支持中文字符的。
所以要想正确存放中文字符,首要条件就是告诉python解释器,要用某种支持中文的编码格式把数据存到硬盘中(存到数据库中是一样的道理,因为,
一般数据库的表中的数据最终都是存到硬盘上的),
这里距离两种情况
一种是,源数据是 utf-8 你自己环境也是 utf-8,使用的是 python2,你要存到数据库中的编码是用 gbk;那就需要下面这样设定:
# -*- coding:utf-8 -*- # 这里要提醒一点的是,你用的文本编辑器的编码需要和这里文件的第一行声明的编码 # 一致(utf-8)。 str_val="中文" # 假设这个变量是 utf8 编码的 # 基本思路是: # 1. 先用原来的字符集解码称 Unicode # 2. 再用想要存储的字符集(这里是 gbk)编码成按照gbk方式编码排列的字节 bytes # 3. 最后把编码后的字节存入硬盘或者数据库 gbk_bytes=str_val.decode('utf-8').encode('gbk') # gbk_bytes 这个变量就是我们需要存储到硬盘或数据库的数据
另一种情况是,你用的还是 python2 ,python程序文件声明的是 utf-8 ,而你从远程主机或者数据库中的字符集却是 gbk,并且你要把这个数据以 utf-8 的形式展现处理或者说打印到屏幕,展现后再存入到 gbk的数据库
# -*- coding:utf-8 -*- # 这里要提醒一点的是,你用的文本编辑器的编码需要和这里文件的第一行声明的编码 # 一致(utf-8)。 remote_str # 这个假设是你从远程主机或数据库得到的字符串,编码是 gbk # 先转成 unicode u_str = remote_str.decode('gbk') # 下面是要打印的数据 print_utf8_str = u_str.encode('utf-8') # 接下来就是你要存的数据 save_gbk_str = remote_str # 因为你取回来的数据就是 gbk 编码的,这里就不用转换 # 或者这样再转换一下 save2_gbk_str = u_str.encode('gbk')
从硬盘读取到内存中,反之亦然。
这个字符编码可以是GBK(gb2312)或者 utf-8。
具体的 实现方法就是在python2的程序(脚本)文件的开始第二行,声明字符编码。
#!/usr/bin/env python # -*- coding:utf-8 –*-
或者
#!/usr/bin/env python #coding=utf-8
#coding:utf8,其实就是告诉解释器,你不要以默认的编码方式去解码这个文件,而是以utf8来解码。
各种编码之间的转换原则:
- 最初数据是用什么编码 encode(比如 gbk)存储的,在进行解码 decode 时也必须使用原来的编码(这里是 gbk)
- 所有的编码进行解码后的数据都是 Unicode 格式,这是存在于所有计算机系统内存中的数据,所有的环境都可以识别出 unicode 编码的数据,不用再解码和转吗
- 在进行转换到其他编码(比如转成 utf-8)之前,必须先转换成 Unicode 编码(即解码 decode);转为 Unicode 编码后才可以再转换为想要的任意编码(这里假设转为 utf-8)
实战经验:
生产中遇到这样的情况,用 ansible 执行 shell 命令 到远程主机,远程主机的编码是采用 GBK
得到的数据保存在一个变量里,变量名假设是 result
打印 result 得到下面的结果
```
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
```
于是想看看它究竟是啥?
```
print repr(stra)
u'\xd3\xca\xcf\xe4\xd7\xe9'
```
这是什么玩意儿!
解决办法:
```
print '>>>>:',stra.encode('raw_unicode_escape').decode('gbk').encode('utf-8')
```
```
print a.encode('latin1')
print a.encode('latin1').decode('utf-8')
print a.encode('raw_unicode_escape')
```
解决方法:
print a.encode('latin1')
print a.encode('latin1').decode('utf-8')
print a.encode('raw_unicode_escape')
参考资料:
https://www.15yan.com/story/ekRmUBlLFMI/
这种数据是如何得到的:
>>> stra='中午'
>>> stra
'\xe4\xb8\xad\xe5\x8d\x88'
>>> print stra
中午
>>> unicode(stra,'unicode_escape')
u'\xe4\xb8\xad\xe5\x8d\x88'
>>> stra.decode('raw_unicode_escape')
u'\xe4\xb8\xad\xe5\x8d\x88'
>>>

浙公网安备 33010602011771号