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来解码。

    各种编码之间的转换原则:

 

  1.   最初数据是用什么编码 encode(比如 gbk)存储的,在进行解码 decode 时也必须使用原来的编码(这里是 gbk)
  2.   所有的编码进行解码后的数据都是 Unicode 格式,这是存在于所有计算机系统内存中的数据,所有的环境都可以识别出 unicode 编码的数据,不用再解码和转吗
  3.   在进行转换到其他编码(比如转成 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'
>>> 

  

 

posted @ 2017-06-15 22:06  西瓜甜  阅读(296)  评论(0)    收藏  举报