第四章 文本和字节序列
今天终于开启第四章啦,这部分也是我平常生活中用的比较多,但是也查的最多的一部分,总是把一些东西搞混,这次系统的学一学,争取拿下!!
我们人类在编程中都是使用文本字符串,不会说用二进制序列、字节序列去编代码。但是我们的计算机却使用的是字节序列,那么中间就需要一个翻译将unicode字符串和二进制序列做转换。
4.1
4.1
4.1 字符问题
字符串,简单来讲,一个字符串就是一个字符序列。
unicode标准将字符的标识和具体的字节表述进行了一下区分:
- 字符的标识——即码位。是0~1114111的数字(十进制),在unicode标准中以4~6个十六进制的数字表示,而且要加前缀U+。
- 字符的具体表示取决于所用的编码。编码是在码位和字节序列之间转换时用到的算法。
举个?:
字母A的码位是U+0041,在UTF-8编码中,A的码位编码为单个字节\x41,而在UTF-16中,编码成了两个字节\x41\x00。
将码位变成字节序列的过程称为编码,把字节序列转换为码位的过程是解码。
示例如下:
>>> s = 'iam刘'
>>> len(s)
4
>>> b = s.encode('utf8')
>>> b
b'iam\xe5\x88\x98'
>>> len(b)
6
>>> b.decode()
'iam刘'
可以得知,汉字被utf-8编码成了3个字节。
解码之后,该序列的长度由原先的4个字节变成了6个字节。
其中 bytes的字面量是以b开头的。
4.2 4.2 4.2 字节概要
python 中内置了两种基本的二进制序列类型:python3的bytes类型和python2.6中添加的bytearray。
bytes和bytearray对象的元素是介于0~255之间的整数。然而二进制的序列切片始终是同一类型的二进制序列,包括长度为1的切片。
示例如下:
>>> name = bytes('iam刘',encoding='utf_8')
>>> name
b'iam\xe5\x88\x98'
>>> name[0]
105
>>> name[:1]
b'i'
>>> name_arr = bytearray(name)
>>> name_arr
bytearray(b'iam\xe5\x88\x98')
>>> name_arr[-1:]
bytearray(b'\x98')
bytes对象的切片还是bytes的对象,bytearray对象的切片还是bytearray对象。我们可以用给定的编码去构建。
虽然二进制的序列其实是整数序列,但是它们的字面量表示法表明其中有ASCII文本。所以各个字节的值可能会使用下列三种不同的方式显示:
- 可打印的ASCII范围内的字节(从空格到~),使用ASCII字节本身。
- 制表符、换行符、回车符和\对应的字节,使用转义序列\t,\n,\r和\。
- 其他字节的值,使用16进制转义字符。
因此,在“iam刘”的实例中,iam在ASCII的范围内可以被直接打印出来,而“刘”则被转义了。
str类型的很多方法都支持bytes和bytearray类型使用。这意味着,我们可以使用熟悉的字符串方法来处理二进制的序列,包括re模块的正则表达式也可以处理二进制序列。
构建bytes或bytearray的方式示例如下:
>>> b = bytearray(2)
>>> b
bytearray(b'\x00\x00')
>>> b = bytes(13)
>>> b
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> b_list = bytes(array.array('i', [-1, -3, 0, 2, 4]))
>>> b_list
b'\xff\xff\xff\xff\xfd\xff\xff\xff\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00'
它可以是一个str对象和一个encoding关键字参数。
也可以是一个可迭代对象,提供0~255之间的数值。
一个整数,使用空字节创建对应长度的二进制序列。
一个实现了缓冲协议的对象;此时是将源对象中的字节序列复制到新建的二进制序列中去。
那么如果你想要从二进制的序列中提取一些结构化的信息,可以使用struct模块。
结构体和内存视图
struct模块提供了一些函数,将打包的字节序列可以转化成不同类型的字段所组成的元组。
>>> import struct
>>> v1 = 1
>>> bytes = struct.pack('i',v1)
>>> bytes
b'\x01\x00\x00\x00'
这里有一个完整的fmt列表:
pack的时候,会按照对应的fmt方式将原值转化为string。
4.3 4.3 4.3 基本的编解码器
python有超过100种的编解码器,用于在文本和字节之间的相互转化。我们常用的如’utf-8’,'utf-16’等都是编解码器的一种。
这些名称,是我们编解码时会用到的参数。
例如:
>>> str = '你好'
>>> str_2 = str.encode('utf-8')
>>> str_2
b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> str3 = str_2.decode('utf-16')
>>> str3
'뷤\ue5a0붥'
你好直接被解码成火星韩语了?
4.4 4.4 4.4 了解编解码问题
处理UnicodeEncodeError——把文本转换成字节序列时,如果目标编码中并不存在某个字符,则会抛出UnicodeEncodeError异常。
示例如下:
>>> city = '?️'
>>> city.encode('utf_8')
b'\xf0\x9f\x89\x90\xef\xb8\x8f'
>>> city.encode('cp437')
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "/usr/local/var/pyenv/versions/3.6.6/lib/python3.6/encodings/cp437.py", line 12, in encode
# return codecs.charmap_encode(input,errors,encoding_map)
# UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-1: character maps to <undefined>
同样的,并不是每一个字符序列都是有效的utf-8或utf-16。所以,我们将二进制序列变成文本时,假设是这两个编码中的一个,那么当遇到无法转换的字节序列时,就会抛出UnicodeDecodeError。
示例如下:
>>> my_byte = b'Montr\xe9al'
>>> my_byte.decode('utf_8')
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# UnicodeDecodeError: 'utf-8' codec can't # decode byte 0xe9 in position 5: invalid # continuation byte