你知道 Python 如何解压缩数据吗?

楔子

当数据量大的时候,自然而然想到的就是对数据进行压缩,下面来看看 Python 如何压缩数据。这里主要介绍三个模块,分别是 zlib、bz2、gzip,它们都是内置的,直接导入即可,不需要额外安装。那么下面就开始吧。

zlib 模块

首先是 zlib:

import zlib

original_data = b"komeiji satori is a cute girl"
print(len(original_data))  # 29

# 对数据进行压缩
compressed_data = zlib.compress(original_data)
# 当数据量少的时候,压缩数据不一定就减少大小
print(len(compressed_data))  # 37
# 解压数据
print(zlib.decompress(compressed_data) == original_data)  # True

# 我们将数据弄的再长一些
for i in range(1, 10):
    data = original_data * i
    compressed_data = zlib.compress(data)
    print(f"压缩前数据长度: {len(data)}, 压缩后数据长度: {len(compressed_data)}")
"""
压缩前数据长度: 29, 压缩后数据长度: 37
压缩前数据长度: 58, 压缩后数据长度: 40
压缩前数据长度: 87, 压缩后数据长度: 40
压缩前数据长度: 116, 压缩后数据长度: 40
压缩前数据长度: 145, 压缩后数据长度: 40
压缩前数据长度: 174, 压缩后数据长度: 40
压缩前数据长度: 203, 压缩后数据长度: 40
压缩前数据长度: 232, 压缩后数据长度: 40
压缩前数据长度: 261, 压缩后数据长度: 40
"""

在压缩的时候还可以指定压缩级别:

import zlib

original_data = b"komeiji satori is a cute girl" * 1024

for i in range(0, 10):
    # 压缩级别 0 ~ 9,不指定为 -1,默认压缩级别
    compressed_data = zlib.compress(original_data, i)
    print(f"压缩前数据长度: {len(original_data)}, 压缩后数据长度: {len(compressed_data)}")
"""
压缩前数据长度: 29696, 压缩后数据长度: 29707
压缩前数据长度: 29696, 压缩后数据长度: 245
压缩前数据长度: 29696, 压缩后数据长度: 245
压缩前数据长度: 29696, 压缩后数据长度: 245
压缩前数据长度: 29696, 压缩后数据长度: 122
压缩前数据长度: 29696, 压缩后数据长度: 122
压缩前数据长度: 29696, 压缩后数据长度: 122
压缩前数据长度: 29696, 压缩后数据长度: 122
压缩前数据长度: 29696, 压缩后数据长度: 122
压缩前数据长度: 29696, 压缩后数据长度: 122
"""

如果数据过大,还可以采用增量压缩,举个栗子:

from io import BytesIO
import zlib

original_data = b"komeiji satori is a cute girl" * 1024
buffer = BytesIO()
buffer.write(original_data)
buffer.seek(0)

# 创建压缩器,压缩等级为 5
compressor = zlib.compressobj(5)
while (data := buffer.read(1024)) != b"":
    # 每次读取 1024 字节,进行压缩
    compressor.compress(data)
# 循环结束之后,压缩结束,调用 flush 方法拿到压缩数据
compressed_data = compressor.flush()
# 这里我们进行解压,然后对比一下,看看和原始数据是否相等
# 注意:zlib 压缩之后是有一个头部信息的,否则会认为数据不是 zlib 压缩格式
# 但是增量压缩会将头部信息给去掉,所以解压的时候还要手动加上
# 这个头部信息是 b"x\x9c",转成十六进制就是 "789c"
decompressed_data = zlib.decompress(b"x\x9c" + compressed_data)
print(original_data == decompressed_data)  # True

当然啦,压缩数据还可以和原始数据混在一起,举个栗子:

import zlib

original_data = b"komeiji satori is a cute girl" * 1024
compressed_data = zlib.compress(original_data)
combined_data = compressed_data + original_data
# 创建一个解压缩器
decompressor = zlib.decompressobj()
# 对 combined_data 进行解压,只会得到对 compressed_data 解压之后的数据
# 由于 zlib 会在压缩数据前面加上一个头部信息,所以 combined_data 要求必须是压缩数据在前
decompressed_data = decompressor.decompress(combined_data)
# 和原始数据是相等的
print(decompressed_data == original_data)  # True
# 还可以拿到未解压的数据,显然也是原始数据
print(decompressor.unused_data == original_data)  # True

校验数据完整性:

import zlib

original_data = b"komeiji satori is a cute girl" * 1024
# 这两个方法还可以接收第二个参数,也就是初始值
print(zlib.adler32(original_data))  # 4170046071
print(zlib.crc32(original_data))  # 2627291461

bz2 模块

bz2 模块和 zlib 的用法非常类似:

import bz2

original_data = b"komeiji satori is a cute girl" * 1024
# 也可以指定压缩等级,范围 1 ~ 9,注意:zlib 是 0 ~ 9
compressed_data = bz2.compress(original_data, 5)
print(len(original_data))  # 29696
print(len(compressed_data))  # 103
print(bz2.decompress(compressed_data) == original_data)  # True

同样可以增量压缩:

from io import BytesIO
import bz2

original_data = b"komeiji satori is a cute girl" * 1024
buffer = BytesIO()
buffer.write(original_data)
buffer.seek(0)

# 创建压缩器,压缩等级为 5
compressor = bz2.BZ2Compressor(5)
while (data := buffer.read(1024)) != b"":
    compressor.compress(data)
compressed_data = compressor.flush()
# 这里不需要额外补充头部信息
decompressed_data = bz2.decompress(compressed_data)
print(original_data == decompressed_data)  # True

也可以同时包含压缩数据和未压缩数据:

import bz2

original_data = b"komeiji satori is a cute girl" * 1024
compressed_data = bz2.compress(original_data)
combined_data = compressed_data + original_data
# 创建一个解压缩器
decompressor = bz2.BZ2Decompressor()
decompressed_data = decompressor.decompress(combined_data)
# 和原始数据是相等的
print(decompressed_data == original_data)  # True
# 还可以拿到未解压的数据,显然也是原始数据
print(decompressor.unused_data == original_data)  # True
# 同样要求压缩数据在前

相比 zlib,bz2 还可以读写文件:

import os
import bz2

original_data = b"komeiji satori is a cute girl" * 1024
# 写入文件
with bz2.open("1.bz2", "wb", compresslevel=9) as f:
    f.write(original_data)
# 读取文件,判断两者是否相等
with bz2.open("1.bz2", "rb", compresslevel=9) as f:
    print(f.read() == original_data)  # True

os.unlink("1.bz2")

当然啦,我们使用内置函数 open、然后手动写入压缩数据或者读取数据再手动解压,也是可以的。另外,我们不光可以使用二进制读取,还可以通过文本模式。

original_data = b"komeiji satori is a cute girl" * 1024

with bz2.open("1.bz2", "w", encoding="utf-8", compresslevel=9) as f:
    f.write(original_data)
with bz2.open("1.bz2", "r", encoding="utf-8", compresslevel=9) as f:
    print(f.read() == original_data)  # True

bz2 还有一个类 BZ2File,也是用于打开文件,不过我们一般用的还是 open,当然 open 底层也是调用的 BZ2File。

gzip 模块

首先 gzip 只有全量压缩,没有增量压缩。

import gzip

original_data = b"komeiji satori is a cute girl" * 1024
# 也可以指定压缩等级,范围 0 ~ 9,默认为 -1
compressed_data = gzip.compress(original_data, 5)
print(len(original_data))  # 29696
print(len(compressed_data))  # 134
print(gzip.decompress(compressed_data) == original_data)  # True

还可以调用 open 函数:

import os
import gzip

original_data = b"komeiji satori is a cute girl" * 1024
# 写入文件
with gzip.open("1.bz2", "wb", compresslevel=9) as f:
    f.write(original_data)
# 读取文件,判断两者是否相等
with gzip.open("1.bz2", "rb", compresslevel=9) as f:
    print(f.read() == original_data)  # True

os.unlink("1.bz2")

gzip 还有一个类 GzipFile,也是用于打开文件,不过我们一般用的还是 open,当然 open 底层也是调用的 GzipFile。

小结

以上就是 Python 压缩数据所使用的三个模块,之间是比较相似的。另外再补充一句,一般将数据压缩之后,会转成 16 进制进行传输,举个栗子:

import binascii
import gzip

original_data = b"komeiji satori"
compressed_data = gzip.compress(original_data, 5)

hex_data = binascii.hexlify(compressed_data)
print(
    hex_data
)  # b'1f8b080058391e6200ffcbcecf4dcdccca54284e2cc92fca0400b46b50ef0e000000'
print(
    binascii.unhexlify(hex_data) == compressed_data
)  # True

还是比较简单的。

posted @ 2019-11-09 13:48  古明地盆  阅读(768)  评论(0编辑  收藏  举报