bytes 类型
在 Python 2 中,有两种类型的字符代表字符串,分别是 Strings 和 Unicode。默认将 Strings 以 utf-8 编码成 bytes 类型,而不是使用 unicode 编码(Python 3 已经把 String 使用 Unicode 编码,Unicode 就是 string,而 bytes 就是 bytes),且存在一个类型为 Unicode 类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
>>> u'徐' # 显示字符 Unicode code, 十六进制 u'\u5f90' >>> type(u'徐') <type 'unicode'> >>> str('中') '\xe4\xb8\xad' >>> bytes('中') '\xe4\xb8\xad' >>> type(str('中')) str >>> type(bytes('中')) str |
可以看到 Python 2 中 strings 等同于 bytes 类型。
Python 3 最重要的新特性大概要算是对文本和二进制数据作了更为清晰的区分。在内存中的 str 类型就是 Unicode(具体 str 类型存储到磁盘或通过网络发送出去时可以是 utf8 编码后的二进制数据),二进制数据则由 bytes 类型表示。字符串是以字符为单位进行处理的,bytes 类型是以单个字节作为基本元素(8 位,取值范围 0-255)为单位处理的。
bytes 对象是由单个字节构成的不可变序列,可以把 bytes 看作是一个特殊的数组,由连续的字节(byte)组成,单字节最大数不能超过 255,具有数组的切片,迭代等特性。 由于许多主要二进制协议都基于 ASCII 文本编码,因此 bytes 字面值和表示法是基于 ASCII 文本的,bytes 对象提供了一些仅在处理 ASCII 兼容数据时可用,并且在许多特性上与字符串对象紧密相关的方法。
bytes 对象只负责以二进制字节序列的形式来存储数据,至于这些数据到底表示什么内容(字符串、数字、图片、音频等),完全由程序的解析方式决定。如果采用合适的字符编码方式(字符集),字节串可以恢复成字符串;反之亦然,字符串也可以转换成字节串。
总结来说,在 Python 3 版本中,字符串是以 Unicode 编码的,也就是说,Python 的字符串支持多语言,例如:
1 2 |
>>> print('包含中文的str') 包含中文的str |
对于单个字符的编码,Python 提供了ord()
函数获取字符在 Unicode 中的十进制整数表示,chr()
函数把编码转换为对应的字符:
1 2 3 4 5 |
>>> a = '徐' >>> ord(a) # 返回一个字符的 Unicode code 24464 >>> chr(24464) # 返回一个 Unicode code 对应的字符 '徐' |
chr()
函数能接受 Unicode code 的形式可以是十进制,二进制,十六进制等,效果都是一样的:
1 2 |
>>> chr(24464), chr(0x5f90), chr(0b101111110010000) # 0x 表示十进制, 0b 表示二进制 ('徐', '徐', '徐') |
怎么转换对应进制呢?Python 也提供了对应的方法相互转换。
1 2 3 4 5 6 7 8 9 10 11 |
# bin 方法可以把十进制转为二进制 >>> bin(24464) '0b101111110010000' # hex 方法可以把十进制转为二进制 >>> hex(24464) '0x5f90' # int 方法可以把二进制或十六进制转为十进制 >>> int(0x5f90), int(0b101111110010000) (24464, 24464) |
在 Python 中,用非零开头的数字,表示十进制,0x 开头的表示十六进制,0b 开头的表示二进制,需要先识别出对应的进制类型,然后才能做对应的转换,这很好理解;或者你告诉 int 方法,你传入的是什么类型。
1 2 |
>>> int('24464', 10), int('0x5f90', 16), int('0b101111110010000', 2) (24464, 24464, 24464) |
如果知道字符的整数编码,还可以用十六进制这么写 str:
1 2 |
>>> '\u5f90' '徐' |
两种写法完全是等价的。
由于 Python 3 的字符串类型是 str,在内存中以 Unicode 表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把 str 变为以字节为单位的 bytes,我们可以通过字符串来创建 bytes 对象,或者说将字符串转换成 bytes 对象。bytes 类型的数据非常适合在互联网上传输,可以用于网络通信编程;bytes 也可以用来存储图片、音频、视频等二进制格式的文件。
对于 ASCII 字符串,表示 bytes 字面值的语法与字符串字面值的大致相同,只是添加了一个 b
前缀,可以直接使用 b’xxxx’ 赋值创建 bytes 对象,但对于非 ASCII 编码的字符则不能通过这种方式创建 bytes 实例。任何超出 127 的二进制值必须使用相应的转义序列形式加入 bytes 字面值。
原因上面说明过了,由于许多主要二进制协议都基于 ASCII 文本编码,因此 bytes 对象提供了一些仅在处理 ASCII 兼容数据时可用。
1 2 3 4 5 6 7 |
>>> d = b'徐' File "<console>", line 1 SyntaxError: bytes can only contain ASCII literal characters. >>> d = b'ywnds' >>> print(d) b'ywnds' |
通常我们可以通过调用 bytes() 类(没错,它是类,不是函数)生成 bytes 实例,其值形式为 b’xxxxx’,其中 ‘xxxxx’ 为一至多个转义的十六进制字符串(单个 x 的形式为:\xHH,其中 \x 为小写的十六进制转义字符,HH 为二位十六进制数)组成的序列,由于两个十六进制数码精确对应一个字节,因此十六进制数是描述二进制数据的常用格式(八位二进制数,取值范围 0-255),对于同一个字符串如果采用不同的编码方式生成 bytes 对象,就会形成不同的值。
1 2 3 4 5 6 7 8 9 10 11 |
>>> a = '徐' >>> b = bytes(a, 'utf-8') >>> type(b) # 获取 b 类型(如果是 Python 2 这里是 str) <class 'bytes'> >>> print(b) b'\xe5\xbe\x90' >>> bytes(a, 'gb2312') b'\xd0\xec' |
比如上例中的 a 字符串对象,其十进制 unicode code 为 24464,分别使用 ‘utf-8’ 和 ‘gb2312’ 两种编码格式将其转换成 bytes 对象 b 和 c ,结果 b 和 c 的值是完全不同的,由于基于的编码格式不一致,b 和 c 的长度甚至都不相同,前者有 3 个字节长度,后者有 2 个字节长度。
1 2 |
>>> print(len(b), len(c)) 3 2 |
除了上面两种方法可以将 Unicode 表示的 str 转换为指定编码的 bytes 类型外,也可以以 Unicode 表示的 str 通过 encode() 方法编码为指定的 bytes,默认为 utf8 编码,例如:
1 2 3 4 5 6 7 8 |
>>> 'ABC'.encode('ascii') b'ABC' >>> '中文'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87' >>> '中文'.encode('ascii') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128) |
纯英文的 str 可以用 ASCII 编码为 bytes,内容是一样的,含有中文的 str 可以用 UTF-8 编码为 bytes。含有中文的 str 无法用 ASCII 编码,因为中文编码的范围超过了 ASCII 编码的范围,Python 会报错。
在 bytes 中,它总是尝试以 ASCII 编码将数据转成可显示字符,这主要是由于许多主要二进制协议都基于 ASCII 文本编码,因此 bytes 对象提供了一些仅在处理 ASCII 兼容数据时可用。超出 ASCII 可显示范围内的字节用\x##
显示,##
表示二位十六进制,一个字节。
1 2 |
>>> '中文abc'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87abc' |
反过来,一般我们从网络或磁盘上读取的字节流数据就是 bytes。对于 bytes 实例,如果需要还原成相应的字符串,则需要借助内置的解码函数 decode(),借助相应的编码格式解码为正常字符串对象,如果采用错误的编码格式解码,则有可能发生错误。
1 2 3 4 |
>>> b'\xe5\xbe\x90'.decode('utf8') '徐' >>> b'\xd0\xec'.decode('gb2312') '徐' |
如果bytes
中包含无法解码的字节,decode()
方法会报错:
1 2 3 4 |
>>> b'\xe5\xbe\x90'.decode('gb2312') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'gb2312' codec can't decode byte 0x90 in position 2: incomplete multibyte sequence |
如果bytes
中只有一小部分无效的字节,可以传入errors='ignore'
忽略错误的字节:
1 2 |
>>> b'\xe5\xbe\x90'.decode('gb2312', errors='ignore') '寰' |
由于 bytes 是序列,因此我们可以通过索引或切片访问它的元素:
1 2 3 4 |
>>> d[0] 121 >>> d[0:1] b'y' |
可以发现如果以单个索引的形式访问元素,其会直接返回单个字节的十进制整数,而以序列片段的形式访问时,则返回相应的 bytes 中 ASCII 索引元素。
对于 bytes,我们只要知道在 Python 3 中某些场合下强制使用,以及它和字符串类型之间的互相转换,其它的基本照抄字符串。
由于字符类型的不同,所以你通常会需要两个工具函数来对这两种情况的字符进行转换,以此来确保输入值符合代码所预期的字符类型。
Python 3 中写一个方法,接收 str 或者 bytes,总是来返回 str 类型的数据。如下:
1 2 3 4 5 6 7 |
def to_str(bytes_or_str): if isinstance(bytes_or_str, bytes): value = bytes_or_str.encode('utf-8') else: value = bytes_or_str # str类型的数据 return value |
同理,我们需要另一个方法,来接收 str 或 bytes ,总是来返回 bytes 类型的数据。
1 2 3 4 5 6 7 |
def to_bytes(bytes_or_str): if isinstance(bytes_or_str, str): value = bytes_or_str.encode('utf-8') else: value = bytes_or_str # 字节类型的数据 return value |
在 Python 3 中,bytes 类型包含的是 8 个比特值的序列,str 是包含 Unicode 字符串。字节和字符串实例不能同时出现在操作符 ‘>’ 或者 ‘+’ 中,这两者无法比较。所以最好使用工具函数来确保程序输入的数据是预期类型。
bytearray
bytearray 对象是 bytes 对象的可变字节数组。bytearray 对象没有专属的字面值语法,它们总是通过调用构造器来创建。
编码和解码十六进制数
如果你只是简单的解码或编码一个十六进制的原始字符串,可以使用binascii
模块。例如:
1 2 3 4 5 6 7 8 9 10 |
# Initial byte string >>> s = b'hello' # Encode as hex >>> import binascii >>> h = binascii.b2a_hex(s) >>> h b'68656c6c6f' # Decode back to bytes >>> binascii.a2b_hex(h) b'hello' |
类似的功能同样可以在 base64
模块中找到。例如:
1 2 3 4 5 |
>>> h = base64.b16encode(s) >>> h b'68656C6C6F' >>> base64.b16decode(h) b'hello' |
大部分情况下,通过使用上述的函数来转换十六进制是很简单的。 上面两种技术的主要不同在于大小写的处理。 函数 base64.b16decode()
和 base64.b16encode()
只能操作大写形式的十六进制字母, 而 binascii
模块中的函数大小写都能处理。
1 2 3 4 5 |
>>> h = base64.b16encode(s) >>> print(h) b'68656C6C6F' >>> print(h.decode('ascii')) 68656C6C6F |
在解码十六进制数时,函数 b16decode()
和 a2b_hex()
可以接受字节或unicode字符串。 但是,unicode 字符串必须仅仅只包含 ASCII 编码的十六进制数。
<原文>
https://www.liaoxuefeng.com/wiki/1016959663602400/1017075323632896
https://python3-cookbook.readthedocs.io/zh_CN/latest/chapters/p06_data_encoding_and_process.html