一篇文章读懂:编码、解码、存储单位,以及Python3.x检测和设置编码

标签: python3  编码  解码  utf-8  存储单位

如需转载请注明出处。
win10+Python 3.6.3

1、编码 是约定的一个协议。是一种用二进制数据表示抽象字符的方式。
计算机本质上只认识 0 和 1 (二进制数字),能通过计算机显示出文本,这就靠编码实现。ASCII编码 约定大写字母A对应十进制数65,在计算机读取字符串时,遇到65,计算就知道这是大写字母A。

ASCII American Standard Code for Information Interchange,美国信息交换标志代码,是基于拉丁字母(A-Ya-y)的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。
ASCII编码设计时 只采用1个字节存储(实际上只用了7位,一个字节有8位),包含大小写英文字母、数字、一些符号。
一个ASCII码 就是一个字节。换算到十进制,范围在 -127 至 127(2的7次方)。

GB2312 是对ASCII的中文扩展(兼容ASCII),是中国规定的汉字编码,即简体中文的字符集编码(支持的汉字比较少)。

GBK 汉字内码扩展规范,GBK是国标 扩展拼音第一个字母。是微软对GB2312的扩展,向下兼容GB2312,是微软标准但不是国家标准。还能显示繁体中文、日文假名。
参考

Unicode Unite code,万国码。是一种编码规范。
一个英文字符/标点 站2个字节;一个汉字(含繁体)/标点占 2个字节
(若文本只包含英文和数字,使用Unicode编码就会浪费存储空间,ASCII编码只占用一半的存储空间。因此Unicode创建出了多种实现方式,如UTF-8)

UTF-8 8 bit Unicode Transformation Format,Unicode转换格式,是Unicode的实现方式之一,是互联网上使用最广的一种Unicode的实现方式,是一种可变长的编码方式:
1)对于单字节的符号,字节第一位设为0,后面7位为此符号的Unicode码。因此,对于英文字母,UTF-8码 和ASCII码是相同的。
2)对于n字节的符号(n>1),第1个字节的前n位都设为1,第n+1位设为0;后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为此符号的unicode码。
一个英文字符/标点 占 1个字节;一个汉字(含繁体)/标点占 3个字节
参考

Unicode与utf-8的关系:
Unicode是内存编码表示方案(是规范);而uft-8如何保存和传输Unicode的方案(是实现),节省硬盘和流量。

无论是gbk/utf-8,它们都只是一种编码规则,一种把Unicode数据编码成字节数据的规则。因此 utf-8编码的字节一定要用utf-8的规则解码,否则会出现乱码或报错。

存储单位:
1)bit:位。 1个二进制数据0 或1,是1bit。计算机中最小的数据单位;
2)byte:字节。存储空间的基本计量单位。1 byte = 8 bit。
1个英文字符占1个字节 1字母=1byte= 8bit
1个汉字 占2个字节 1汉字=2byte=16bit

标点符号英文输入状态下,默认为半角输入方式;
汉字输入状态下,默认为全角输入方式;
全角输入方式下,1个标点符号占2个字节;
半角输入方式下,1个标点符号占1个字节。

3)KB:kilobyte 千字节。1 KB =1000 byte
4)MB: Megabyte 兆字节。1 MB =1000 KB
5)GB:Gigabyte 吉字节。1 GB =1000 MB
6)TB:Terabye 太字节。1 TB =1000 GB

2、编码转换
明文:人类很容易理解的消息。
密文:将人类不易懂,但更易于存储和传输的消息。
这里写图片描述
encode 编码——–write写入时,需编码
decode 解码——–read读取时,需解码
detect 译作 “检测”

编码转换前提是:知道这个字符串当前使用的是什么编码。否则是进行解码是会出错的。
编码、解码,都有一个原则:不能丢失任何信息。
明文 编码成 密文 需要按照一定的编码方式,编码方式(ASCII/GB2312/GBK/Unicode等)有多种多样,分别对应于不同的字符集。使用Python解释器IDEL进行如下编码、解码操作,在bytes、str之间转换:

>>> str1 = "编码学习"
>>> str1.encode()#python3.x默认编码格式utf-8,可不写入参数。编码过程
b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'
>>> str1.encode('gb2312')
b'\xb1\xe0\xc2\xeb\xd1\xa7\xcf\xb0'
>>> bytes(str1, 'utf-8')#bytes()#函数作用同str1.encode(),即将str类型的数据 编码为bytes类型
b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'
>>> b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'.decode()#默认为utf-8,可不写入参数。解码过程
'编码学习'
>>> b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'.encode()#不能把btyes数据继续编码为bytes。没这个方法。
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'.encode()
AttributeError: 'bytes' object has no attribute 'encode'
>>> str1.decode()#同样,不能把str 继续编码为 str。也没这个方法。
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    str1.decode()
AttributeError: 'str' object has no attribute 'decode'
>>> b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'.decode('gb2312')#以utf-8编码的bytes数据以gb2312解码会报错!
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    b'\xe7\xbc\x96\xe7\xa0\x81\xe5\xad\xa6\xe4\xb9\xa0'.decode('gb2312')
UnicodeDecodeError: 'gb2312' codec can't decode byte 0x96 in position 2: illegal multibyte sequence
>>> 

在使用Python以二进制的形式写入文件时,需要先将字符串编码成字串,然后再写入文件。
以二进制的形式读取文件时也是如此,需要将读取的字节串解码成字符串。

Python3.x的编码哲学:
有一个很重要的新特性是:对文本、二进制数据 作了更为清晰的区分,不再会对bytes字节串进行自动解码。
文本总是以Unicode编码,由str类型表示;二进制数据由bytes数据表示。

在字符串前加’b’,它就被明确表示为一个bytes类型的对象,实质是一组二进制字节序列组成的数据(数据可以是 ASCII范围内的字符、其他十六进制形式的字符数据。但:不能是中文 等非ASCII字符)

>>> s = b'我'
SyntaxError: bytes can only contain ASCII literal characters.

Python3.x不会以任意隐式的方式混用str和bytes,正是这使得两者区分特别清晰。这也意味着不可拼接字符串 和字节包、也无法在字节包里搜索字符串(反之亦然)、也不能将字符串传入参数为字节包的函数中(反之亦然)。当然:其他操作 如分片、索引、基本数值运算,bytes和str类型提供的操作是一样的。

>>> s1 = '牛'
>>> s2 = u'牛'
>>> type(s1)
<class 'str'>
>>> type(s2)
<class 'str'>
>>> s3 = b'hh'
>>> type(s3)
<class 'bytes'>
>>> s1 +s3
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    s1 +s3
TypeError: must be str, not bytes
>>> 

Python3.x,默认编码、编码方式均为 encoding=”utf-8”。(Python2.x默认为ASCII)

>>> import sys
>>> sys.getdefaultencoding()#查看当前编码方式(Python3.x默认为utf-8)
'utf-8'
>>> sys.setdefaultencoding('utf-8')#设置编码方式。当前有效,重启解释即会失效。

三种方案 “一劳永逸”设置默认的某种编码方式
1)在Python/Lib/site-packages文件夹下新建sitecustomize.py,系统在启动python时,会自动调用该文件,设置系统的默认编码,内容为:

# encoding = utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

2)在程序中加入上述1)代码。
3)程序中所有涉及到编码之处,强制编码为你需要的编码方式,如encode(‘utf-8’),但不推荐,因为一旦某处未写,将会导致异常。

无论Python2.x还是Python3.x,与明文直接对应的都是Unicode数据,打印Unicode数据就会显示相应的明文(包括中文、英文)。
这里写图片描述
图中string编码在CPU执行程序时的存储状态,是另外一个过程。不可混淆。
上图是编码流程。文件从磁盘到内存的编码:

  • 在文本编辑器(如word/txt/Sublime text3/IDEL/各种解释器等等)编辑文字(中文/英文)时,计算机是不认识这些的。在我们保存这份文件数据之前,数据是以Unicode形式存在内存中的。
  • 保存了的存到磁盘上的数据 是通过某种编码方式(如utf-8/gbk等)进行编码(encode) 过的bytes字节串。(在保存操作时编辑软件默默地作了编码工作,有它们默认保存文件的编码方式)
  • 再次打开文件 时,软件又默默地(从磁盘)做了解码(decode)工作,即将数据从byte解码成Unicode(到内存)。然后以明文呈现给用户。Unicode是离用户更近的数据,byte是离计算机更近的数据。

Phtyhon运行.py文件过程:打开文件,解码存磁盘的bytes数据成Unicode数据;接着,解释器将Unicode数据翻译成C代码,再转成二进制的数据流;最后,通过控制操作系统调用CPU执行二进制数据,得出结果。整个过程结束。

Python社区中有一个优秀的非官方模块 chardet,char detect简写,检测字符串的编码。可检测的编码有:

  • ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants)
  • Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese)
  • EUC-JP, SHIFT_JIS, CP932, ISO-2022-JP (Japanese)
  • EUC-KR, ISO-2022-KR (Korean)
  • KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic)
  • ISO-8859-2, windows-1250 (Hungarian)
  • ISO-8859-5, windows-1251 (Bulgarian)
  • windows-1252 (English)
  • ISO-8859-7, windows-1253 (Greek)
  • ISO-8859-8, windows-1255 (Visual and Logical Hebrew)
  • TIS-620 (Thai)

chardet安装方法:在cmd下直接输入,注意不是Python环境下。

  • pip install chardet
  • 下载安装包,并解压,cd 进入所在文件夹,输入:‪D:\Python\Python363\python.exe setup.py install
  • 更新update:pip install -U chardet

实践:

>>> import urllib.request as urlreq
>>> response = urlreq.urlopen("https://www.baidu.com").read()
>>> import chardet
>>> chardet.detect(response)
{'encoding': 'ascii', 'confidence': 1.0, 'language': ''}
>>> 

得到一个字典:
encoding 编码格式;
confidence 可信度,1.0表示100%(但不一定准确),提供的检测内容足够长,值越高;
language 语言

若使用的GBK编码(GBK是GB2312的扩展),但dectect()显示为GB2312,decode(“GB2312”)可能会报错 UnicodeDecodeError。解决方案有两种:
1)忽略识别不出的字符(GB2312支持的汉字较少,若使用GB2312编码可能会出现小部分乱码情况);

>>>response.decode("GB2312", "ignore")

2)(推荐)GBK是向下兼容GB2312。如 若detect()得到是GB2312,可直接用GBK来编码/解码。

>>>if chardet.detect(response)['encoding'] == 'GB2312'
response.decode('GBK')

如在编码时遇到一些问题,可参考小甲鱼的一篇文章,或许有你要的答案:Python编码问题的解决方案总结

如需转载请注明出处。

版权声明:本文为weixin_38256474原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_38256474/article/details/81317304