Python encode 与 decode 深入理解

前言

在理解encode和decode之前,必须先知道以下几个概念:

  • ascii、gb2312、gbk、gb18030、utf8、unicode:上面这些均是字符编码规范;其中gb2312、gbk、gb18030是我国出的规范,其他的是外国人出的规范;
    例如‘姜’字,用gbk、utf8或unicode编码(encode操作),在内存中占的字节是不一样的,所以文件出现乱码,是文件打开时使用的编码格式不对。
  • str和bytes是字符串类型,是程序语言的基本数据类型、和int、dict、list是一个级别的。

encode()

encode() 方法为字符串类型(str)提供的方法,用于将 str 类型转换成 bytes 类型,这个过程也称为“编码”。
encode() 方法的语法格式如下:


str.encode([encoding="utf-8"][,errors="strict"])

# [] 中表示是可选参数

# encoding = "utf-8" 
# 指定进行编码时采用的字符编码,该选项默认采用 utf-8 编码。例如,如果想使用简体中文,可以设置 gb2312。
# 当方法中只使用这一个参数时,可以省略前边的“encoding=”,直接写编码格式,例如 str.encode("UTF-8")。

# errors = "strict"
# 指定错误处理方式,其可选择值可以是:
# - strict:遇到非法字符就抛出异常。
# - ignore:忽略非法字符。
# - replace:用“?”替换非法字符。
# - xmlcharrefreplace:使用 xml 的字符引用。
# 该参数的默认值为 strict。

decode()

和 encode() 方法正好相反,decode() 方法用于将 bytes 类型的二进制数据转换为 str 类型,这个过程也称为“解码”。
decode() 方法的语法格式如下:


bytes.decode([encoding="utf-8"][,errors="strict"])

# [] 中表示是可选参数

# encoding="utf-8"
# 指定解码时采用的字符编码,默认采用 utf-8 格式。当方法中只使用这一个参数时,可以省略“encoding=”,直接写编码方式即可。
# 注意,对 bytes 类型数据解码,要选择和当初编码时一样的格式。

# errors = "strict"
# 用法同 encode()

其他

  1. encoding 的可选值 参考《官方文档》,常用的选项有ascii、gb2312、gbk、gb18030、utf_8、utf_8_sig、unicode_escape
  2. encoding='utf_8'encoding='utf8'encoding='utf-8',这三种写法是一样的
  3. 个人理解见下:
>>> s = "C语言中文网"
>>> s.encode()
b'C\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91'
>>> s.encode().decode()
C语言中文网

上面示例中,'C语言中文网'.encode()之后的打印出来为b'xxx'格式,是因为encode()之后数据为bytes类型,bytes类型数据在控制台的表现形式就是以b''开头的。
至于'C\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91',是一堆二进制数据的表示形式,
由ascii字符+16进制数字组成,一个ascii字符或一个16进制数字代表一个字节,例如,'C'表示一个字节,\xe8表示另一个字节(\x表示16进制的意思),
utf8编码中,字母、数字、常用符号的编码同ascii编码是一样的,占一个字节;汉字的编码是用三个字节表示的,
所以,'C'在utf8编码中对应'\x43','语'在utf8编码中对应是'\xe8\xaf\xad',其他字符以次类推,就得到了'C\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91',这个过程就是编码,也就是encode()。
注意的是:'C'打印出来是'C',而不是'\x43',只是打印时为了区分一个字节utf8和3个字节的utf8,直接使用\x43是一样的效果,均可以正常解码。

例如:

>>> b'\x43\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91'.decode() 
'C语言中文网'
>>> b'C\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91'.decode()    
'C语言中文网'
>>> 

如果不知道编码情况下,单独看一个字节是没意义的;例如,'\xe8'的16进制是e8,10进制是232,2进制是11101000,仅仅是一个数字。
但是,如果知道是utf8编码时,就对这些数字赋予了意义,例如'\xe8\xaf\xad'是三个字节,在utf8编码中对应的是汉字‘语’;
从b'\xe8\xaf\xad'转换成‘语’的过程就是编码,也就是decode()。

遇到的问题及解决方案:

问题1:b'\x43\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91' utf8解码的过程中,怎么知道该取一个字节解码还是取3个字符解码?

一个字节的utf8,一定是在\x01\x7f之间,翻译成10进制就是1127;反过来讲就是,在\x01~\x7f之间字节,是一定表示一个字节的utf8;
三个字节的utf8中每一个字节,一定是在\x80\xff之间,翻译成10进制就是128255。反过来讲就是, 在\x80~\xff之间的字节,是一定表示3个字节的utf8;
所以在解码的过程中,取每一个字节时会判断区间,然后就知道解码成一个一字节还是三个字节了

问题2:我有一堆'\x43\xe8\xaf\xad\xe8\xa8\x80\xe4\xb8\xad\xe6\x96\x87\xe7\xbd\x91'的数据,怎么能正常显示出来?

第一步,先把这堆str按原二进制位转成bytes类型

s = 'C\xd3\xef\xd1\xd4\xd6\xd0\xce\xc4\xcd\xf8'    # 注意s是str类型,非bytes
>>> s.encode('raw_unicode_escape') 
b'C\xd3\xef\xd1\xd4\xd6\xd0\xce\xc4\xcd\xf8'        

第二步,尝试用各种编码规则解码,如果能正常解析,就知道了这堆字符串的正确编码

>>> s.encode('raw_unicode_escape').decode('utf8')  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd3 in position 1: invalid continuation byte
>>> s.encode('raw_unicode_escape').decode('gb2312') 
'C语言中文网'
>>> s.encode('raw_unicode_escape').decode('gbk')    
'C语言中文网'
>>> s.encode('raw_unicode_escape').decode('gb18030') 
'C语言中文网'

参考:http://c.biancheng.net/view/4305.html

posted @ 2021-07-16 20:29  ugvibib  阅读(1352)  评论(0编辑  收藏  举报