目录
经常遇到 bytes 对象的使用会懵逼,故写点笔记。
bytes
str 转 bytes
相对于字符串以字符为单元进行操作,bytes 则以字节为单元进行操作,可以理解为“字节串”(注意,这不是专业术语,只是便于理解的叫法)。看一段代码:
mybytes = b"hello world" // (1)
mybytes2 = b"好好学习" // (2)error
mybytes3 = bytes("好好学习", encoding="utf8") // (3)
mybytes4 = bytes("hello 世界", encoding="utf-8") // (4)
mybytes5 = "hello 世界".encode("utf-8") // (5)
(1)字符串前加个 "b" 就是将 "hello world" 字符串转换为 bytes 对象。但最迷惑的是,这段字符串以什么样的字节存储。答案是:如果字符串是一串 ASCII 字符,那么就存储它们的 ASCII 值。所以这里 mybytes 存储的是:
h e l l o <空格> w o r l d
01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111 01110010 01101100 01100100
(2)如果字符串是一串非 ASCII 码,那么不能使用前缀 b 将字符串转换成 bytes 对象:
错误信息标明只能包含 ASCII 码范围内的字符。
(3)所以,必须用构造函数将字符串转换成 bytes 对象,并且要指定第二个参数,也就是字符集。可以这样理解,我们现在所看到的字符串,例如"好好学习",只是 4 个字符图像,实际在内存中存储什么样的字节内容,就得看给程序指定了哪种字符集,比如指定 utf8:
UTF-8 用 3 个字节存储汉字,所以字节串长度就有 4*3=12 个。默认情况下,python 使用的字符集是 UTF-8。
(4)如果字符串既有 ASCII 字符,又有汉字,那么仍然需要指定字符集。
UTF-8 对 ASCII 字符用一个字节存储,用三个字节存储汉字,所以这里 6+3*2=12 个字节。注意,在控制台输出 bytes 对象时,对 ASCII 字符仍然是打印它们的字符图像,而其他非 ASCII 字符就打印它们的十六进制。
bytes 转 str
用 bytes 对象的 decode() 方法将字节串重新转换成字符串。默认使用 UTF-8 解码,所以一般将字符串转换为 bytes 对象时指定 UTF-8 字符集:
>>> b = bytes("hello 世界",encoding="utf-8")
>>> b
b'hello \xe4\xb8\x96\xe7\x95\x8c'
>>> b.decode()
'hello 世界'
>>> b.decode(encoding="utf-8")
'hello 世界'
当然,也可以用其他字符集,例如 GBK,不过 decode() 就必须指定同样的字符集,否则可能会报错:
>>> b = bytes("hello 世界", encoding="gbk")
>>> b
b'hello \xca\xc0\xbd\xe7'
>>> b.decode()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xca in position 6: invalid continuation byte
>>> b.decode(encoding="gbk")
'hello 世界'
hex 转 bytes
现在有一个十六进制字符串:
str = "342ec70264b61f9749aa17558239eddb"
这只是一个以字符串的形式表示一串 0 与 1,注意只是形式,str 变量实际存储的是每个字符的 ASCII 码:
3 4 2 e ...
00110011 00110100 00110010 01100101 01....
但我们希望存储的是十六进制转换成的二进制,也就是这样的:
3 4 2 e ..
0011 0100 0010 1110 11...
那么这就不能用字符串类型存储了,而是用 bytes 对象。用 bytes.fromhex() 可以将十六进制字符串转换成对应的二进制字节串:
>>> bytes.fromhex("342ec70264b61f9749aa17558239eddb")
b'4.\xc7\x02d\xb6\x1f\x97I\xaa\x17U\x829\xed\xdb'
bytes 转 hex
bytes.hex() 将二进制字节串用十六进制字符串表示:
>>> bytes.fromhex("342ec70264b61f9749aa17558239eddb")
b'4.\xc7\x02d\xb6\x1f\x97I\xaa\x17U\x829\xed\xdb'
>>> bytes.fromhex("342ec70264b61f9749aa17558239eddb").hex()
'342ec70264b61f9749aa17558239eddb'
编码解码
base64
使用 base64 模块,方法:
- base64.b64encode(bytes)
- base64.b64decode(str)
例子:
>>> import base64
>>> base64.b64encode(b"hello world")
b'aGVsbG8gd29ybGQ='
>>> base64.b64decode("aGVsbG8gd29ybGQ=")
b'hello world'
如果有非 ASCII 字符,就用 bytes 构造函数:
>>> b = bytes("hello 世界", encoding="utf-8")
>>> base64.b64encode(b)
b'aGVsbG8g5LiW55WM'
>>> base64.b64decode("aGVsbG8g5LiW55WM")
b'hello \xe4\xb8\x96\xe7\x95\x8c'
>>> base64.b64decode("aGVsbG8g5LiW55WM").decode()
'hello 世界'
base64 解码后得到的是 bytes 对象,可以调用 decode() 方法将其转换为字符串。
URL
用到 urllib.quote 模块,方法:
- quote(str)
- unquote(str)
例子:
>>> from urllib.parse import quote,unquote,urlencode
>>> quote("好好学习")
'%E5%A5%BD%E5%A5%BD%E5%AD%A6%E4%B9%A0'
>>> unquote("%E5%A5%BD%E5%A5%BD%E5%AD%A6%E4%B9%A0")
'好好学习'
默认使用 UTF-8 字符集。不过 quote() 并非完全编码,只是对非 URL 规定的字符编码,例如:
>>> quote("+,ab'")
'%2B%2Cab%27
其中 a 和 b 就没有被 URL。所以,如果进行完全 URL 编码,可以遍历每个字符,得到它们的十六进制数字,然后在前面拼接 "%"。
参考
http://c.biancheng.net/view/2175.html