python中base64库详解
我们知道在计算机中任何数据都是按字节存储的,有的复杂的数据(比如汉字)可能由多个字节表示,简单(比如单个英文字符)的由 1 个字节表示,字节是内存中基本的存储单位。
我们知道一个字节可表示的范围是 0 ~ 255(十六进制:0x00 ~ 0xFF),其中 ascii 值的范围为 0 ~ 127(十六进制:0x00 ~ 0x7F);而超越 ascii 范围的 128~255(十六进制:0x80 ~ 0xFF)之间的值是不可见字符。当然也并不是所有的 ascii 都是可见的,ascii 中只有 95 个可见字符(范围为 32 ~ 126),其余的 ascii 为不可见字符,本节后面我们会列出所有可见的 ascii 和 不可见的 ascii。
当不可见字节流在网络上传输时,比如说从 A 计算机传到 B 计算机,往往要经过多个路由设备,由于不同的设备(特指老的路由设备)对字节流的处理方式有一些不同,这样那些不可见字节就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个 base64 编码,统统变成可见字节,也就是 ascii 码可表示的可见字符,确保数据可靠传输。base64 的内容是有 0 ~9,a ~z,A ~Z,+,/组成,正好 64 个字符,这些字符是在 ascii 可表示的范围内,属于 95 个可见字符的一部分。
对于现在的路由设备,只要是文本字符(无论是否是可见字符)都可以转为字节流(字节类型数据)在网络上传输。而字节类型的数据(图片,音频,视频,语音等二进制数据), 本身就是字节流,所以可以直接在网络上传输。但是,在企业开发中,我们为了一致性约定,往往对要发送的数据先进行序列化,然后再编码(encoding)为字节流在网络上发送,而对方收到数据后,要先把字节流解码(decoding)为字符串,然后再反序列化还原为原始数据。
我们往往使用 json 对要发送的数据进行序列化,因为 json 有多语言支持,而 pickle 只有 Python 语言本身支持,我们知道通信的另一方不一定也是使用 Python 语言,所以,我们对网络传输的数据一般不使用 pickle 进行序列化。然而,json 无法序列化字节类型的数据,那我们如果想发送字节类型的数据(图片,音频,视频,语音等二进制数据),应该怎么办呢?
有些同学可能会想,我们可以把字节类型数据转为字符串类型数据,然后再使用 json 序列化,然而不幸的是,对于字节类型的图片,音频,视频,语音等二进制数据里面含有很多不可见字节,而这些不可见字节无法转为字符串。但是,我们可以先用 base64 把这些不可见字节数据编码为可见字节数据,然后再转为字符串类型,然后在通过 json 进行序列化,然后再编码为字节流通过网络发送出去。而对方收到数据后,要先把字节流解码为字符串,然后再反序列化,然后再转为字节类型数据,然后再使用 base64 解码还原成原始数据。
最后,大家可能要问,字节类型的数据(图片,音频,视频,语音等二进制数据)本身就可以直接通过网络传输,为什么还要序列化?在此,我再告诉大家,你项目中要传输的数据不只有这些二进制吧,应该还需要包容其它类型的数据(int,list,dict等等),而这些结构化类型数据,必须要序列化后,才能通过网络传输。通信双方约定的就是发送的东西都是序列化后数据,这样方便对通信双方的数据类型进行还原。你没必要只对其它类型的数据序列化,对字节类型数据不序列化,这样反而增加了业务逻辑。
base64 的使用
base64 是 Python 内建模块,我们只需要 import 导入模块就可以使用,我们可以把超越 ascii 范围的字符用 base64 编码为可见字符,注意 base64只能编码字节类型数据。
import base64 mydata = b"\x80" # \x80 换成十进制为 128,超越了 ascii 范围,为不可见字节 datab64 = base64.b64encode(mydata) # 编码为 base64 的可见字节 print(datab64) mydata = base64.b64decode(datab64) print(mydata) # 解码为不可见字节 \x80
base64 自己有一套算法可以把我们的字节编码成 ascii 范围内的字节,当然它不关心我们给的字节是不是可见字节,如果是可见字节同样要编码,如果是不可见字节则会编码成可见字节。
import base64
dataone = "p".encode("utf8")    # 可见字节
datatwo = "共".encode("utf8")   # 不可见字节
print(base64.b64encode(dataone))  # 编码可见字节为可见字节
print(base64.b64encode(datatwo))  # 编码不可见字节为可见字节
base64 编码会把 3 个字节的字节类型数据编码为 4 个可见字节的字节类型数据,如果要编码的字节类型数据不是 3 的倍数,最后会剩下 1 个或 2 个字节,base64 会在编码的末尾加上 1 个或 2 个 = 号,表示补了多少字节,解码的时候,会自动去掉。
import base64
strone = "p".encode("utf8")             # 1个字节
strtwo = "py".encode("utf8")            # 2 个字节
strthree = "pyt".encode("utf8")         # 3 个字节
strfour = "pyth".encode("utf8")         # 4 个字节
strfive = "pytho".encode("utf8")        # 5 个字节
strsix = "python".encode("utf8")        # 6 个字节
strseven = "毛票票".encode("utf8")        # utf-8 编码,每个汉字一般占 3 个字节
streight = "毛票票python".encode("utf8")  # utf-8 编码,每个汉字一般占 3 个字节,英文占 1 个字节
print(base64.b64encode(strone))    # 按 3 个字节(缺 2 个,补齐 2 个 =)编码成 4 个字节 cA==
print(base64.b64encode(strtwo))    # 按 3 个字节(缺 1 个,补齐 1 个 =)编码成 4 个字节 cHk=
print(base64.b64encode(strthree))  # 按 3 个字节编码成 4 个字节 cHl0
print(base64.b64encode(strfour))   # 按 6 个字节(缺 2 个,补齐 2 个 =)编码成 8 个字节 cHl0aA==
print(base64.b64encode(strfive))   # 按 6 个字节(缺 1 个,补齐 1 个 =)编码成 8 个字节 cHl0aG8=
print(base64.b64encode(strsix))    # 按 6 个字节编码成 8 个字节 cHl0aG9u
print(base64.b64encode(strseven))  # 按 6 个字节编码成 8 个字节 6ICB6bif
print(base64.b64encode(streight))  # 按 12 个字节编码成 16 个字节 6ICB6bifcHl0aG9u
我们可以对用 base64 编码后的字节进行解码。
import base64
dataone = "python".encode("utf8")    # 可见字节
datatwo = "毛票票".encode("utf8")       # 不可见字节
dataone = base64.b64encode(dataone)  # 编码可见字节 "python"
datatwo = base64.b64encode(datatwo)  # 编码不可见字节 "老鸟" 为可见字节
print(base64.b64decode(dataone))     # 解码 base64 编码后的字节
print(base64.b64decode(datatwo))     # 解码 base64 编码后的字节
由于 base64 编码后可能出现字符 + 和 /,在网页上传输数据,我们用 get 方式传输字符串时,字符 + 和 / 在 URL 中有特殊用途,就不能直接作为参数,所以又有一种 "url safe" 的 base64 编码,其实就是把字符 + 和 / 分别变成 - 和 _。
import base64 mydata = b"\xfb\xef\xff" print(base64.b64encode(mydata)) # 编码为 ++//,但是 + 和 / 在 url 中有特殊用途,不能作为参数 print(base64.urlsafe_b64encode(mydata)) # 编码为 --__,很好。
在互联网上传输图片,音乐,视频,语音等等这些二进制数据是常用的需求, 本节开头部分,我们说过,最好要对所有发送的数据进行序列化后再进行网络传输。所以,正规的流程是:先用 base64 把这些不可见字节数据编码为可见字节数据,然后再转为字符串类型,然后在通过 json 进行序列化,然后再编码为字节流通过网络发送出去。而对方收到数据后,要先把字节流解码为字符串,然后再反序列化,然后再转为字节类型数据,然后再使用 base64 解码还原成原始数据。
import base64
import json
f = open("d:/test.png", "rb")  # 确保你计算机中存在 d:/test.png
imgdata = f.read()             # 二进制图片数据
f.close()
imgdatab64 = base64.b64encode(imgdata)     # 编码为可见字节
imgdatab64str = imgdatab64.decode("utf8")  # 转为字符串
jsondata = json.dumps(imgdatab64str)       # json 序列化
bytesdata = jsondata.encode("utf8")        # 编码为字节类型在网络上传输
'''
通过网络传输......,
对方机器收到后,先反序列化,最后用 base64 进行解码
'''
bytesdata_recv = bytesdata                     # 收到网络上发来的字节流
jsondata_recv = bytesdata_recv.decode("utf8")  # 解码为字符串
strdata_recv = json.loads(jsondata_recv)       # json 反序列化
bytesdata_recv = strdata_recv.encode("utf8")   # 转为字节类型数据
imgdata = base64.b64decode(bytesdata_recv)     # 解码为原始数据
# 把从网络上收到的图片数据写入磁盘,文件名字为 copy.png
f = open("d:/copy.png", "wb")
imgdata = f.write(imgdatab64)
f.close()
注意:base64 仅仅是把字节类型数据按照一定的算法转化为属于 ascii 范围内的可见字节类型数据,但不要作为加密行为。
ascii 可见和不可见字符表
我们知道一个字节的大小超越 ascii 编码的都是不可见字符,但 ascii 编码也不是所有字符都是可见的,在 128 个 ascii 中,只有 95 个可见字符,下表为 ascii 中可见字符表。
| 
 | 
 | 
 | 
下表为 ascii 不可见字符表。
| 二进制 | 十进制 | 十六进制 | 缩写 | 可以显示的表示法 | 名称/意义 | 
|---|---|---|---|---|---|
| 0000 0000 | 0 | 00 | NUL | ␀ | 空字符(Null) | 
| 0000 0001 | 1 | 01 | SOH | ␁ | 标题开始 | 
| 0000 0010 | 2 | 02 | STX | ␂ | 本文开始 | 
| 0000 0011 | 3 | 03 | ETX | ␃ | 本文结束 | 
| 0000 0100 | 4 | 04 | EOT | ␄ | 传输结束 | 
| 0000 0101 | 5 | 05 | ENQ | ␅ | 请求 | 
| 0000 0110 | 6 | 06 | ACK | ␆ | 确认回应 | 
| 0000 0111 | 7 | 07 | BEL | ␇ | 响铃 | 
| 0000 1000 | 8 | 08 | BS | ␈ | 退格 | 
| 0000 1001 | 9 | 09 | HT | ␉ | 水平定位符号 | 
| 0000 1010 | 10 | 0A | LF | ␊ | 换行键 | 
| 0000 1011 | 11 | 0B | VT | ␋ | 垂直定位符号 | 
| 0000 1100 | 12 | 0C | FF | ␌ | 换页键 | 
| 0000 1101 | 13 | 0D | CR | ␍ | 归位键 | 
| 0000 1110 | 14 | 0E | SO | ␎ | 取消变换(Shift out) | 
| 0000 1111 | 15 | 0F | SI | ␏ | 启用变换(Shift in) | 
| 0001 0000 | 16 | 10 | DLE | ␐ | 跳出数据通讯 | 
| 0001 0001 | 17 | 11 | DC1 | ␑ | 设备控制一(XON 启用软件速度控制) | 
| 0001 0010 | 18 | 12 | DC2 | ␒ | 设备控制二 | 
| 0001 0011 | 19 | 13 | DC3 | ␓ | 设备控制三(XOFF 停用软件速度控制) | 
| 0001 0100 | 20 | 14 | DC4 | ␔ | 设备控制四 | 
| 0001 0101 | 21 | 15 | NAK | ␕ | 确认失败回应 | 
| 0001 0110 | 22 | 16 | SYN | ␖ | 同步用暂停 | 
| 0001 0111 | 23 | 17 | ETB | ␗ | 区块传输结束 | 
| 0001 1000 | 24 | 18 | CAN | ␘ | 取消 | 
| 0001 1001 | 25 | 19 | EM | ␙ | 连接介质中断 | 
| 0001 1010 | 26 | 1A | SUB | ␚ | 替换 | 
| 0001 1011 | 27 | 1B | ESC | ␛ | 跳出 | 
| 0001 1100 | 28 | 1C | FS | ␜ | 文件分割符 | 
| 0001 1101 | 29 | 1D | GS | ␝ | 组群分隔符 | 
| 0001 1110 | 30 | 1E | RS | ␞ | 记录分隔符 | 
| 0001 1111 | 31 | 1F | US | ␟ | 单元分隔符 | 
| 0111 1111 | 127 | 7F | DEL | ␡ | 删除 | 
本节重要知识点
会使用 base64 进行编解码。
清楚 base64 使用的场景。


 
								  
								   
	 
	 
	 
	
评论列表