第89集:文件压缩与解压
学习目标
- 掌握常见的文件压缩格式(ZIP、TAR、GZIP)
- 学会使用zipfile模块进行ZIP文件操作
- 掌握tarfile模块进行TAR文件操作
- 了解gzip模块进行GZIP压缩
- 学会批量压缩和解压文件
- 了解压缩文件的最佳实践
一、文件压缩基础
1.1 什么是文件压缩
文件压缩是通过算法减少文件大小的过程,可以节省存储空间和传输时间。常见的压缩格式包括:
- ZIP:最常用的压缩格式,支持跨平台
- TAR:Unix/Linux常用的归档格式
- GZIP:常用的压缩算法,常与TAR结合使用
- RAR:私有格式,需要专门的解压工具
1.2 压缩与归档
- 压缩:减少文件大小
- 归档:将多个文件打包成一个文件
- 压缩归档:既打包又压缩
1.3 Python压缩模块
- zipfile:处理ZIP文件
- tarfile:处理TAR文件
- gzip:处理GZIP压缩
- bz2:处理BZIP2压缩
- lzma:处理LZMA压缩
二、zipfile模块详解
zipfile模块是Python标准库中处理ZIP文件的主要模块。
2.1 创建ZIP文件
基本用法
import zipfile
# 创建ZIP文件
with zipfile.ZipFile('archive.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
# 添加单个文件
zf.write('file.txt')
# 添加多个文件
zf.write('document.pdf')
zf.write('image.png')添加文件并重命名
import zipfile
with zipfile.ZipFile('archive.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
# 添加文件并重命名
zf.write('file.txt', arcname='renamed.txt')
# 添加文件到子目录
zf.write('file.txt', arcname='folder/file.txt')压缩级别
import zipfile
# 压缩级别:0-9,0不压缩,9最大压缩
with zipfile.ZipFile('archive.zip', 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
zf.write('file.txt')2.2 读取ZIP文件
列出ZIP文件内容
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
# 列出所有文件
print(zf.namelist())
# 获取文件信息
for info in zf.infolist():
print(f"文件名: {info.filename}")
print(f"文件大小: {info.file_size}")
print(f"压缩后大小: {info.compress_size}")
print(f"压缩时间: {info.date_time}")
print("-" * 50)读取ZIP文件中的内容
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
# 读取文本文件
content = zf.read('file.txt').decode('utf-8')
print(content)
# 读取二进制文件
binary_data = zf.read('image.png')2.3 解压ZIP文件
解压所有文件
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
# 解压到当前目录
zf.extractall()
# 解压到指定目录
zf.extractall('extracted')解压单个文件
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
# 解压单个文件
zf.extract('file.txt')
# 解压到指定位置
zf.extract('file.txt', 'output')2.4 ZIP文件操作
检查ZIP文件
import zipfile
# 检查是否为有效的ZIP文件
if zipfile.is_zipfile('archive.zip'):
print("这是一个有效的ZIP文件")
# 检查ZIP文件中的文件
with zipfile.ZipFile('archive.zip', 'r') as zf:
if 'file.txt' in zf.namelist():
print("文件存在于ZIP中")向现有ZIP文件添加文件
import zipfile
# 追加模式
with zipfile.ZipFile('archive.zip', 'a', zipfile.ZIP_DEFLATED) as zf:
zf.write('new_file.txt')从ZIP文件中删除文件
import zipfile
import tempfile
import os
# 创建临时ZIP文件
with zipfile.ZipFile('archive.zip', 'r') as zf:
with zipfile.ZipFile('temp.zip', 'w', zipfile.ZIP_DEFLATED) as new_zf:
for item in zf.infolist():
if item.filename != 'file_to_delete.txt':
new_zf.writestr(item, zf.read(item.filename))
# 替换原文件
os.replace('temp.zip', 'archive.zip')2.5 ZIP文件信息
获取ZIP文件信息
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as zf:
# 获取文件数量
print(f"文件数量: {len(zf.namelist())}")
# 获取总大小
total_size = sum(info.file_size for info in zf.infolist())
print(f"总大小: {total_size} 字节")
# 获取压缩后总大小
total_compressed = sum(info.compress_size for info in zf.infolist())
print(f"压缩后大小: {total_compressed} 字节")
# 计算压缩率
compression_ratio = (1 - total_compressed / total_size) * 100
print(f"压缩率: {compression_ratio:.2f}%")三、tarfile模块详解
tarfile模块用于处理TAR归档文件,支持多种压缩格式。
3.1 创建TAR文件
创建未压缩的TAR文件
import tarfile
# 创建TAR文件
with tarfile.open('archive.tar', 'w') as tf:
# 添加文件
tf.add('file.txt')
# 添加目录
tf.add('folder')
# 添加文件并重命名
tf.add('file.txt', arcname='renamed.txt')创建压缩的TAR文件
import tarfile
# 使用GZIP压缩
with tarfile.open('archive.tar.gz', 'w:gz') as tf:
tf.add('file.txt')
# 使用BZIP2压缩
with tarfile.open('archive.tar.bz2', 'w:bz2') as tf:
tf.add('file.txt')
# 使用XZ压缩
with tarfile.open('archive.tar.xz', 'w:xz') as tf:
tf.add('file.txt')设置压缩级别
import tarfile
# GZIP压缩级别:1-9,默认9
with tarfile.open('archive.tar.gz', 'w:gz', compresslevel=9) as tf:
tf.add('file.txt')3.2 读取TAR文件
列出TAR文件内容
import tarfile
with tarfile.open('archive.tar.gz', 'r:gz') as tf:
# 列出所有文件
print(tf.getnames())
# 获取文件信息
for member in tf.getmembers():
print(f"文件名: {member.name}")
print(f"文件大小: {member.size}")
print(f"修改时间: {member.mtime}")
print(f"是否为目录: {member.isdir()}")
print("-" * 50)读取TAR文件中的内容
import tarfile
with tarfile.open('archive.tar.gz', 'r:gz') as tf:
# 提取文件对象
file_obj = tf.extractfile('file.txt')
content = file_obj.read().decode('utf-8')
print(content)3.3 解压TAR文件
解压所有文件
import tarfile
with tarfile.open('archive.tar.gz', 'r:gz') as tf:
# 解压到当前目录
tf.extractall()
# 解压到指定目录
tf.extractall('extracted')解压单个文件
import tarfile
with tarfile.open('archive.tar.gz', 'r:gz') as tf:
# 解压单个文件
tf.extract('file.txt')
# 解压到指定位置
tf.extract('file.txt', 'output')3.4 TAR文件操作
检查TAR文件
import tarfile
# 检查是否为有效的TAR文件
if tarfile.is_tarfile('archive.tar.gz'):
print("这是一个有效的TAR文件")
# 检查TAR文件中的文件
with tarfile.open('archive.tar.gz', 'r:gz') as tf:
if 'file.txt' in tf.getnames():
print("文件存在于TAR中")添加文件到现有TAR文件
import tarfile
# 追加模式(仅适用于未压缩的TAR文件)
with tarfile.open('archive.tar', 'a') as tf:
tf.add('new_file.txt')3.5 TAR文件信息
获取TAR文件信息
import tarfile
with tarfile.open('archive.tar.gz', 'r:gz') as tf:
# 获取文件数量
print(f"文件数量: {len(tf.getmembers())}")
# 获取总大小
total_size = sum(member.size for member in tf.getmembers())
print(f"总大小: {total_size} 字节")四、gzip模块详解
gzip模块用于处理GZIP压缩文件。
4.1 压缩文件
基本用法
import gzip
# 压缩文本文件
with open('file.txt', 'rb') as f_in:
with gzip.open('file.txt.gz', 'wb') as f_out:
f_out.writelines(f_in)压缩二进制文件
import gzip
# 压缩二进制文件
with open('image.png', 'rb') as f_in:
with gzip.open('image.png.gz', 'wb') as f_out:
f_out.write(f_in.read())设置压缩级别
import gzip
# 压缩级别:1-9,默认6
with open('file.txt', 'rb') as f_in:
with gzip.open('file.txt.gz', 'wb', compresslevel=9) as f_out:
f_out.write(f_in.read())4.2 解压文件
基本用法
import gzip
# 解压文件
with gzip.open('file.txt.gz', 'rb') as f_in:
with open('file.txt', 'wb') as f_out:
f_out.write(f_in.read())解压文本文件
import gzip
# 解压文本文件
with gzip.open('file.txt.gz', 'rt', encoding='utf-8') as f:
content = f.read()
print(content)4.3 GZIP文件信息
获取压缩文件信息
import gzip
# 获取压缩文件大小
import os
compressed_size = os.path.getsize('file.txt.gz')
print(f"压缩文件大小: {compressed_size} 字节")
# 获取原始文件大小
with gzip.open('file.txt.gz', 'rb') as f:
# 读取所有数据
data = f.read()
original_size = len(data)
print(f"原始文件大小: {original_size} 字节")五、实际应用场景
5.1 批量压缩文件
import zipfile
from pathlib import Path
def compress_files(file_list, output_zip):
"""批量压缩文件"""
with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
for file_path in file_list:
if Path(file_path).exists():
zf.write(file_path)
print(f"添加: {file_path}")
else:
print(f"文件不存在: {file_path}")
# 使用
files = ['file1.txt', 'file2.txt', 'file3.txt']
compress_files(files, 'batch_archive.zip')5.2 压缩目录
import zipfile
import os
def compress_directory(directory, output_zip):
"""压缩整个目录"""
with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, directory)
zf.write(file_path, arcname)
print(f"添加: {arcname}")
# 使用
compress_directory('my_folder', 'folder_archive.zip')5.3 解压到指定目录
import zipfile
import os
def extract_to_directory(zip_file, target_dir):
"""解压ZIP文件到指定目录"""
if not os.path.exists(target_dir):
os.makedirs(target_dir)
with zipfile.ZipFile(zip_file, 'r') as zf:
zf.extractall(target_dir)
print(f"解压到: {target_dir}")
# 使用
extract_to_directory('archive.zip', 'extracted')5.4 压缩并加密ZIP文件
import zipfile
def compress_with_password(file_list, output_zip, password):
"""创建带密码的ZIP文件"""
with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
for file_path in file_list:
zf.write(file_path)
# 设置密码
zf.setpassword(password.encode('utf-8'))
print(f"创建加密ZIP: {output_zip}")
# 使用
compress_with_password(['file1.txt', 'file2.txt'], 'secure.zip', 'mypassword')5.5 解压加密ZIP文件
import zipfile
def extract_encrypted(zip_file, password, target_dir):
"""解压加密的ZIP文件"""
with zipfile.ZipFile(zip_file, 'r') as zf:
# 设置密码
zf.setpassword(password.encode('utf-8'))
zf.extractall(target_dir)
print(f"解压到: {target_dir}")
# 使用
extract_encrypted('secure.zip', 'mypassword', 'extracted')5.6 压缩率对比
import zipfile
import tarfile
import gzip
import os
def compare_compression(file_path):
"""比较不同压缩格式的压缩率"""
original_size = os.path.getsize(file_path)
# ZIP压缩
with zipfile.ZipFile('test.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
zf.write(file_path)
zip_size = os.path.getsize('test.zip')
# TAR.GZ压缩
with tarfile.open('test.tar.gz', 'w:gz') as tf:
tf.add(file_path)
tar_gz_size = os.path.getsize('test.tar.gz')
# GZIP压缩
with open(file_path, 'rb') as f_in:
with gzip.open('test.gz', 'wb') as f_out:
f_out.write(f_in.read())
gz_size = os.path.getsize('test.gz')
# 打印结果
print(f"原始大小: {original_size} 字节")
print(f"ZIP大小: {zip_size} 字节 ({zip_size/original_size*100:.2f}%)")
print(f"TAR.GZ大小: {tar_gz_size} 字节 ({tar_gz_size/original_size*100:.2f}%)")
print(f"GZ大小: {gz_size} 字节 ({gz_size/original_size*100:.2f}%)")
# 清理
os.remove('test.zip')
os.remove('test.tar.gz')
os.remove('test.gz')
# 使用
compare_compression('file.txt')5.7 自动备份脚本
import zipfile
import os
from datetime import datetime
def backup_directory(source_dir, backup_dir):
"""备份目录"""
# 创建备份文件名
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_file = os.path.join(backup_dir, f'backup_{timestamp}.zip')
# 确保备份目录存在
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
# 压缩目录
with zipfile.ZipFile(backup_file, 'w', zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(source_dir):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, source_dir)
zf.write(file_path, arcname)
print(f"备份完成: {backup_file}")
print(f"备份大小: {os.path.getsize(backup_file)} 字节")
# 使用
backup_directory('my_project', 'backups')六、压缩文件的最佳实践
6.1 选择合适的压缩格式
- ZIP:跨平台兼容性好,适合一般用途
- TAR.GZ:Linux系统常用,压缩率高
- TAR.BZ2:压缩率更高,但速度较慢
- TAR.XZ:压缩率最高,但速度最慢
6.2 设置合适的压缩级别
- 快速压缩:使用较低的压缩级别(1-3)
- 高压缩率:使用较高的压缩级别(7-9)
- 平衡:使用中等压缩级别(4-6)
6.3 使用上下文管理器
import zipfile
# 好的做法
with zipfile.ZipFile('archive.zip', 'w') as zf:
zf.write('file.txt')
# 不好的做法
zf = zipfile.ZipFile('archive.zip', 'w')
zf.write('file.txt')
zf.close()6.4 处理大文件
import zipfile
# 使用缓冲区处理大文件
buffer_size = 8192
with zipfile.ZipFile('archive.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
with open('large_file.bin', 'rb') as f:
zf.writestr('large_file.bin', f.read())6.5 错误处理
import zipfile
try:
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.extractall()
except zipfile.BadZipFile:
print("错误的ZIP文件")
except Exception as e:
print(f"解压错误: {e}")6.6 验证压缩文件
import zipfile
def verify_zip(zip_file):
"""验证ZIP文件完整性"""
try:
with zipfile.ZipFile(zip_file, 'r') as zf:
# 测试所有文件
bad_file = zf.testzip()
if bad_file is None:
print("ZIP文件完整")
else:
print(f"损坏的文件: {bad_file}")
except zipfile.BadZipFile:
print("错误的ZIP文件")
# 使用
verify_zip('archive.zip')七、常见问题与解决方案
7.1 压缩文件损坏
问题:解压时提示文件损坏
解决方案:
import zipfile
try:
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.extractall()
except zipfile.BadZipFile:
print("ZIP文件已损坏,请重新下载")7.2 文件名编码问题
问题:压缩文件中的中文文件名显示乱码
解决方案:
import zipfile
# 使用正确的编码
with zipfile.ZipFile('archive.zip', 'r') as zf:
for name in zf.namelist():
# 处理编码
try:
print(name.encode('cp437').decode('gbk'))
except:
print(name)7.3 内存不足
问题:压缩大文件时内存不足
解决方案:
import zipfile
# 分块处理
with zipfile.ZipFile('archive.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
with open('large_file.bin', 'rb') as f:
while True:
chunk = f.read(8192)
if not chunk:
break
zf.writestr('large_file.bin', chunk)7.4 压缩速度慢
问题:压缩大量文件时速度很慢
解决方案:
import zipfile
# 使用较低的压缩级别
with zipfile.ZipFile('archive.zip', 'w', zipfile.ZIP_DEFLATED, compresslevel=1) as zf:
zf.write('file.txt')7.5 权限问题
问题:解压时没有权限
解决方案:
import zipfile
import os
# 检查权限
if os.access('extracted', os.W_OK):
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.extractall('extracted')
else:
print("没有写入权限")八、总结
文件压缩与解压是Python编程中的重要技能。本集我们学习了:
- zipfile模块:处理ZIP文件的创建、读取和解压
- tarfile模块:处理TAR归档文件,支持多种压缩格式
- gzip模块:处理GZIP压缩文件
- 实际应用:批量压缩、目录压缩、加密压缩、备份脚本
- 最佳实践:选择合适格式、设置压缩级别、错误处理
掌握这些技能将帮助你有效地管理文件,节省存储空间和传输时间。
九、练习题
- 编写一个函数,将指定目录中的所有文件压缩成ZIP文件
- 实现一个函数,解压ZIP文件到指定目录
- 编写代码,创建带密码的ZIP文件
- 实现一个函数,比较不同压缩格式的压缩率
- 编写一个自动备份脚本,每天备份指定目录
- 实现一个函数,验证ZIP文件的完整性
- 编写代码,批量解压多个ZIP文件
- 实现一个函数,压缩目录并保留目录结构