首页 > Python > Python 3 标准库实例教程 > 互联网数据处理

13.5. base64 — 使用 ASCII 编码二进制数据

目标:base64 模块含有一些可将二进制数据转译为用 ASCII 子集编码数据的函数,使得这些数据可以用纯文本协议来传递。

base64 , base32 , base16 和 base85 编码将 8 位的单字节转换成 ASCII 范围内可打印的字符值,来兼容那些只支持 ASCII 编码数据的系统,代价是使用更多的位来表示数据,比如 SMTP 。 base 的值表示每个字母在编码中所需要的相应的长度。一些使用略有不同的字母表,但仍然是 URL-安全的原编码系统的变种也是存在的。

Base 64 编码

这是用来编码文本的一个基本例子.

base64_b64encode.py

import base64
import textwrap

# 读取源文件并去掉头部。
with open(__file__, 'r', encoding='utf-8') as input:
    raw = input.read()
    initial_data = raw.split('#end_pymotw_header')[1]

byte_string = initial_data.encode('utf-8')
encoded_data = base64.b64encode(byte_string)

num_initial = len(byte_string)

# 填充字节不超过两个
padding = 3 - (num_initial % 3)

print('{} bytes before encoding'.format(num_initial))
print('Expect {} padding bytes'.format(padding))
print('{} bytes after encoding\n'.format(len(encoded_data)))
print(encoded_data)

输入必须是字节串,因此 unicode 字符串首先用 UTF-8 编码。输出显示原本 185 个字节(按输出来看应该是 184 个字节)的 UTF-8 源数据被扩展编码为 248 个字节。

注意

由库函数编码后的数据是不包含换行符的,但我们人为地将其断行是为了在页面上更好地展示输出。

$ python3 base64_b64encode.py

184 bytes before encoding
Expect 2 padding bytes
248 bytes after encoding

b'CmltcG9ydCBiYXNlNjQKaW1wb3J0IHRleHR3cmFwCgojIExvYWQgdGhpcyBzb3
VyY2UgZmlsZSBhbmQgc3RyaXAgdGhlIGhlYWRlci4Kd2l0aCBvcGVuKF9fZmlsZV
9fLCAncicsIGVuY29kaW5nPSd1dGYtOCcpIGFzIGlucHV0OgogICAgcmF3ID0gaW
5wdXQucmVhZCgpCiAgICBpbml0aWFsX2RhdGEgPSByYXcuc3BsaXQoJw=='

Base 64 解码

b64decode() 将编译后的字符串还原成原来的形式,即用查表的方式将四个字节换为原本的三个。

base64_b64decode.py

import base64

encoded_data = b'VGhpcyBpcyB0aGUgZGF0YSwgaW4gdGhlIGNsZWFyLg=='
decoded_data = base64.b64decode(encoded_data)
print('Encoded :', encoded_data)
print('Decoded :', decoded_data)

在编码过程中,输入的每 24 位(每三个字节)组成一个序列,该序列在输出中都被替换为四个字节。输出末尾处的等号仅表示填充,因为本例中,原字符串的位长度不被 24 整除。

$ python3 base64_b64decode.py

Encoded : b'VGhpcyBpcyB0aGUgZGF0YSwgaW4gdGhlIGNsZWFyLg=='
Decoded : b'This is the data, in the clear.'

b64decode() 返回的值是一个字节串。如果已知内容是文本,那么字节串可以被转换成一个 unicode 对象。然而,用 base 64 编码的意义在于传递二进制数据,因此假定解码后的值是文本并不总是安全的。

URL-安全变种

由于默认的 base64 字母表包含 +/ 这两个字符,而它们在 URLs 中也会被用到,因此有必要采用另一种编码方式来替换这些字符。

base64_urlsafe.py

import base64

encodes_with_pluses = b'\xfb\xef'
encodes_with_slashes = b'\xff\xff'

for original in [encodes_with_pluses, encodes_with_slashes]:
    print('Original         :', repr(original))
    print('Standard encoding:',
          base64.standard_b64encode(original))
    print('URL-safe encoding:',
          base64.urlsafe_b64encode(original))
    print()

+ 被替换为 - ,而 / 则被替换为下划线 (_) 。其他字符保持不变。

$ python3 base64_urlsafe.py

Original         : b'\xfb\xef'
Standard encoding: b'++8='
URL-safe encoding: b'--8='

Original         : b'\xff\xff'
Standard encoding: b'//8='
URL-safe encoding: b'__8='

其他编码

除了 Base64 编码,该模块还提供了其他一些函数来处理 Base85 ,Base32 和 Base16 (hex) 编码的数据.

base64_base32.py

import base64

original_data = b'This is the data, in the clear.'
print('Original:', original_data)

encoded_data = base64.b32encode(original_data)
print('Encoded :', encoded_data)

decoded_data = base64.b32decode(encoded_data)
print('Decoded :', decoded_data)

Base32 字母表包含 ASCII 中所有 26 个大写字母和数字 2 到 7 。

$ python3 base64_base32.py

Original: b'This is the data, in the clear.'
Encoded : b'KRUGS4ZANFZSA5DIMUQGIYLUMEWCA2LOEB2GQZJAMNWGKYLSFY==
===='
Decoded : b'This is the data, in the clear.'

Base16 函数则可以处理十六进位制字母表。

base64_base16.py

import base64

original_data = b'This is the data, in the clear.'
print('Original:', original_data)

encoded_data = base64.b16encode(original_data)
print('Encoded :', encoded_data)

decoded_data = base64.b16decode(encoded_data)
print('Decoded :', decoded_data)

每当减少编码一个字母所用的位数时,编码后的数据就需要更多的空间。

$ python3 base64_base16.py

Original: b'This is the data, in the clear.'
Encoded : b'546869732069732074686520646174612C20696E207468652063
6C6561722E'
Decoded : b'This is the data, in the clear.'

Base85 函数则使用了在空间利用率上面比 Base64 更高效的扩展字母表。

base64_base85.py

import base64

original_data = b'This is the data, in the clear.'
print('Original    : {} bytes {!r}'.format(
    len(original_data), original_data))

b64_data = base64.b64encode(original_data)
print('b64 Encoded : {} bytes {!r}'.format(
    len(b64_data), b64_data))

b85_data = base64.b85encode(original_data)
print('b85 Encoded : {} bytes {!r}'.format(
    len(b85_data), b85_data))

a85_data = base64.a85encode(original_data)
print('a85 Encoded : {} bytes {!r}'.format(
    len(a85_data), a85_data))

在 Mercurial , git 和 PDF 文件格式中使用的 Base85 编码存在多个不同变种。Python 实现了其中两种, b85encode() 实现的是在 Git 和 Mercurial 中使用的版本,而 a85encode() 实现的则是在 PDF 文件中使用的 ASCII85 变种。

$ python3 base64_base85.py

Original    : 31 bytes b'This is the data, in the clear.'
b64 Encoded : 44 bytes b'VGhpcyBpcyB0aGUgZGF0YSwgaW4gdGhlIGNsZWF
yLg=='
b85 Encoded : 39 bytes b'RA^~)AZc?TbZBKDWMOn+EFfuaAarPDAY*K0VR9}
'
a85 Encoded : 39 bytes b'<+oue+DGm>FD,5.A79Rg/0JYE+EV:.+Cf5!@<*t
'

参考