第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编程中的重要技能。本集我们学习了:

  1. zipfile模块:处理ZIP文件的创建、读取和解压
  2. tarfile模块:处理TAR归档文件,支持多种压缩格式
  3. gzip模块:处理GZIP压缩文件
  4. 实际应用:批量压缩、目录压缩、加密压缩、备份脚本
  5. 最佳实践:选择合适格式、设置压缩级别、错误处理

掌握这些技能将帮助你有效地管理文件,节省存储空间和传输时间。

九、练习题

  1. 编写一个函数,将指定目录中的所有文件压缩成ZIP文件
  2. 实现一个函数,解压ZIP文件到指定目录
  3. 编写代码,创建带密码的ZIP文件
  4. 实现一个函数,比较不同压缩格式的压缩率
  5. 编写一个自动备份脚本,每天备份指定目录
  6. 实现一个函数,验证ZIP文件的完整性
  7. 编写代码,批量解压多个ZIP文件
  8. 实现一个函数,压缩目录并保留目录结构
« 上一篇 目录操作 下一篇 » 配置文件操作