程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2022-04(3)

2022-05(6)

用 Python 压缩文件方法汇总

发布于2023-02-04 12:40     阅读(914)     评论(0)     点赞(18)     收藏(1)


Python 提供了几乎为所有现有压缩文件的工具,下面逐一领略。

  • zlib 是一个 Python 库,能够实现 zip 、gzip 格式文件的压缩和解压缩。

  • bz2 模块提供了对 bzip2 格式的压缩支持。它也只对单个文件起作用,因此不能归档。

  • lzma 既是算法的名称,也是 Python 模块。它可以产生比一些旧方法更高的压缩比,并且是 xz (更具体地说是 LZMA2 )背后的算法。

  • gzip 是大多数人都熟悉的应用,此外它也是一个 Python 模块的名称。此模块使用前面提到的 zlib 压缩算法,并充当类似于实用程序 gzip 和 gunzip的接口。

  • shutils 是一个模块,我们通常不把该模块与压缩和解压缩联系在一起。但它提供了处理归档文件的实用方法,便于生成 tar 、 gztar 、 zip 、 bztar 或者 xztar 这些类型的归档文件。

  • 顾名思义,zipfile 允许我们用 Python 中实现 zip 归档,提供了创建、读取、写入或追加 zip 文件所需的所有方法,还提供了便于操作这些文件的类和对象。

  • 和上面的 zipfile 类似, tarfile 这个模块用于实现 tar 归档,可以读取和写入 gzip 、bz2 和 lzma 文件或归档文件。 也支持与常规的 tar 压缩软件能实现的其他功能。

压缩与解压缩

上面列出了很多选择,它们中有一些比较基本,有一些具有许多其他功能,但共同点显然是包含压缩功能。下面就来看看有关基本操作。

先看 zlib ,这是一个相当低级的库,因此可能不太常用,让我们来看看针对整个文件的压缩或解压缩方法。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  1. import zlib, sys
  2. filename_in = "data"
  3. filename_out = "compressed_data"
  4. with open(filename_in, mode="rb") as fin, open(filename_out, mode="wb") as fout:
  5. data = fin.read()
  6. compressed_data = zlib.compress(data, zlib.Z_BEST_COMPRESSION)
  7. print(f"Original size: {sys.getsizeof(data)}")
  8. # Original size: 1000033
  9. print(f"Compressed size: {sys.getsizeof(compressed_data)}")
  10. # Compressed size: 1024
  11. fout.write(compressed_data)
  12. with open(filename_out, mode="rb") as fin:
  13. data = fin.read()
  14. compressed_data = zlib.decompress(data)
  15. print(f"Compressed size: {sys.getsizeof(data)}")
  16. # Compressed size: 1024
  17. print(f"Decompressed size: {sys.getsizeof(compressed_data)}")
  18. # Decompressed size: 1000033

上面的代码中所需要的输入文件,可以用 head -c 1MB </dev/zero > data 指令生成,此文件由零组成且大小为 1MB 。将文件读入内存滞后,用 zlib 中的 compress 方法创建压缩数据。然后将该数据写入输出文件。

为了证明能够恢复数据——解压缩,再次打开上述生成的压缩文件并对其通过 zlibb 的 decompress 方法。通过 print ,可以看到压缩和解压缩数据的大小都是匹配的。

下一个是 bz2 ,它的使用方式与上面的 zlib 非常相似:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  1. import bz2, os, sys
  2. filename_in = "data"
  3. filename_out = "compressed_data.bz2"
  4. with open(filename_in, mode="rb") as fin, bz2.open(filename_out, "wb") as fout:
  5. fout.write(fin.read())
  6. print(f"Uncompressed size: {os.stat(filename_in).st_size}")
  7. # Uncompressed size: 1000000
  8. print(f"Compressed size: {os.stat(filename_out).st_size}")
  9. # Compressed size: 48
  10. with bz2.open(filename_out, "rb") as fin:
  11. data = fin.read()
  12. print(f"Decompressed size: {sys.getsizeof(data)}")
  13. # Decompressed size: 1000033

不出所料,使用方法大同小异。为了显示一些不同之处,在上面的示例中,我们简化了压缩步骤,将其减少到1行,并使用 os.stat来检查文件的大小。

这些低级模块中的最后一个是 lzma ,为了避免反复显示相同的代码,这次执行增量压缩:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  1. import lzma, os
  2. lzc = lzma.LZMACompressor()
  3. # cat /usr/share/dict/words | sort -R | head -c 1MB > data
  4. filename_in = "data"
  5. filename_out = "compressed_data.xz"
  6. with open(filename_in, mode="r") as fin, open(filename_out, "wb") as fout:
  7. for chunk in fin.read(1024):
  8. compressed_chunk = lzc.compress(chunk.encode("ascii"))
  9. fout.write(compressed_chunk)
  10. fout.write(lzc.flush())
  11. print(f"Uncompressed size: {os.stat(filename_in).st_size}")
  12. # Uncompressed size: 972398
  13. print(f"Compressed size: {os.stat(filename_out).st_size}")
  14. # Compressed size: 736
  15. with lzma.open(filename_out, "r") as fin:
  16. words = fin.read().decode("utf-8").split()
  17. print(words[:5])
  18. # ['dabbing', 'hauled', "seediness's", 'Iroquoian', 'vibe']

首先创建一个输入文件,文件中包含从字典中提取的一组单词,该字典在 /usr/share/dict/words 中,这样可以确认解压后的数据与原始数据相同。

然后,我们像前面的示例一样打开输入和输出文件。然而,这一次在 1024 位块中迭代随机数据,并使用 LZMACompressor.compress 方法压缩它们。然后将这些块写入输出文件。在读取和压缩整个文件之后,我们需要调用 flush ,以完成压缩过程、并从压缩器中清除任何剩余数据。

为了证实上述操作的有效性,我们以通常的方式打开并解压缩文件,并从文件中打印出几个单词。

下面要研究高级别的模块。现在使用 gzip 执行相同的任务:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  1. import os, sys, shutil, gzip
  2. filename_in = "data"
  3. filename_out = "compressed_data.tar.gz"
  4. with open(filename_in, "rb") as fin, gzip.open(filename_out, "wb") as fout:
  5. # Reads the file by chunks to avoid exhausting memory
  6. shutil.copyfileobj(fin, fout)
  7. print(f"Uncompressed size: {os.stat(filename_in).st_size}")
  8. # Uncompressed size: 1000000
  9. print(f"Compressed size: {os.stat(filename_out).st_size}")
  10. # Compressed size: 1023
  11. with gzip.open(filename_out, "rb") as fin:
  12. data = fin.read()
  13. print(f"Decompressed size: {sys.getsizeof(data)}")
  14. # Decompressed size: 1000033

在这个例子中,结合了 gzip 和 shutils 。看起来我们所做的批量压缩与之前使用 zlib 或 bz2 的效果相同,但由于 shutil.copyfileobj 方法,我们实现了分块增量压缩,而不必像使用lzma那样循环数据。

gzip 模块的一个优点是:它还提供了命令行接口,我说的不是 Linux gzip 和 gunzip ,而是 Python 中所集成的:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  1. python3 -m gzip -h
  2. usage: gzip.py [-h] [--fast | --best | -d] [file [file ...]]
  3. ...
  4. ls -l data*
  5. -rw-rw-r-- 1 martin martin 1000000 aug 22 18:48 data
  6. # Use fast compression on file "data"
  7. python3 -m gzip --fast data
  8. # File named "data.gz" was generated:
  9. ls -l data*
  10. -rw-rw-r-- 1 martin martin 1000000 aug 22 18:48 data
  11. -rw-rw-r-- 1 martin martin 1008 aug 22 20:50 data.gz

更高效的工具

如果你熟悉 zip 或 tar ,或者必须用其中的一种格式存档,就应该认真阅读下面的内容。除了基本的压缩或解压缩操作外,这两个模块还包括其他的一些实用方法,例如校验、使用密码、在归档文件中列出文件等。所以,很有必要深入研究一番,确保掌握这些技能。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  1. import zipfile
  2. # shuf -n5 /usr/share/dict/words > words.txt
  3. files = ["words1.txt", "words2.txt", "words3.txt", "words4.txt", "words5.txt"]
  4. archive = "archive.zip"
  5. password = b"verysecret"
  6. with zipfile.ZipFile(archive, "w") as zf:
  7. for file in files:
  8. zf.write(file)
  9. zf.setpassword(password)
  10. with zipfile.ZipFile(archive, "r") as zf:
  11. crc_test = zf.testzip()
  12. if crc_test is not None:
  13. print(f"Bad CRC or file headers: {crc_test}")
  14. info = zf.infolist() # also zf.namelist()
  15. print(info)
  16. # See all attributes at https://docs.python.org/3/library/zipfile.html#zipinfo-objects
  17. # [ <ZipInfo filename='words1.txt' filemode='-rw-r--r--' file_size=37>,
  18. # <ZipInfo filename='words2.txt' filemode='-rw-r--r--' file_size=47>,
  19. # ... ]
  20. file = info[0]
  21. with zf.open(file) as f:
  22. print(f.read().decode())
  23. # Olav
  24. # teakettles
  25. # ...
  26. zf.extract(file, "/tmp", pwd=password) # also zf.extractall()

上述代码有点长,它涵盖了 zipfile 模块的所有重要功能。在这段代码中,首先在 with 上下文管理中,以 w 模式使用 ZipFile创建 ZIP 归档文件,然后将文件添加到归档文件中。你会注意到,实际上不需要打开要添加的文件 —— 我们所需要做的就是调用 write 方法,并传入文件名为参数。添加所有文件后,我们还使用 setpassword 方法设置存档密码。

接下来,为了证明这种操作方法的有效性,打开归档文件。在读取任何文件之前,检查CRC和文件头,然后检索存档中所有文件的信息。在本例中,我们只打印 ZipInfo 对象的列表,但你也可以检查其属性,以获得CRC、大小、压缩类型等。

检查完所有文件后,打开并读取其中一个文件。我们看到它具有预期的内容,所以可以继续并将其解压缩都指定路径(/tmp/ )。

除了创建和读取归档文件或普通文件外,ZIP 还允许我们将文件追加到现有的存档中。为此,只需将访问模式更改为 a (追加模式):

  1. 1
  2. 2
  3. 3
  4. 4
  1. with zipfile.ZipFile(archive, "a") as zf:
  2. zf.write("words6.txt")
  3. print(zf.namelist())
  4. # ['words1.txt', 'words2.txt', 'words3.txt', 'words4.txt', 'words5.txt', 'words6.txt']

与 gzip 模块相同,Python的 zipfile 和 tarfile 也提供 CLI 。要执行基本存档和提取,请使用以下命令:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  1. python3 -m zipfile -c arch.zip words1.txt words2.txt # Create
  2. python3 -m zipfile -t arch.zip # Test
  3. Done testing
  4. python3 -m zipfile -e arch.zip /tmp # Extract
  5. ls /tmp/words*
  6. /tmp/words1.txt /tmp/words2.txt

最后但并非最不重要的是 tarfile 模块。此模块类似于 zipfile ,但也实现了一些额外的功能:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  1. import tarfile
  2. files = ["words1.txt", "words2.txt", "words3.txt", "words4.txt"]
  3. archive = "archive.tar.gz"
  4. with tarfile.open(archive, "w:gz") as tar:
  5. for file in files:
  6. tar.add(file) # can also be dir (added recursively), symlink, etc
  7. print(f"archive contains: {tar.getmembers()}")
  8. # [<TarInfo 'words1.txt' at 0x7f71ed74f8e0>,
  9. # <TarInfo 'words2.txt' at 0x7f71ed74f9a8>
  10. # ... ]
  11. info = tar.gettarinfo("words1.txt") # Other Linux attributes - https://docs.python.org/3/library/tarfile.html#tarinfo-objects
  12. print(f"{tar.name} contains {info.name} with permissions {oct(info.mode)[-3:]}, size: {info.size} and owner: {info.uid}:{info.gid}")
  13. # .../archive.tar contains words1.txt with permissions 644, size: 37 and owner: 500:500
  14. def change_permissions(tarinfo):
  15. tarinfo.mode = 0o100600 # -rw-------.
  16. return tarinfo
  17. tar.add("words5.txt", filter=change_permissions)
  18. tar.list()
  19. # -rw-r--r-- martin/martin 37 2021-08-23 09:01:56 words1.txt
  20. # -rw-r--r-- martin/martin 47 2021-08-23 09:02:06 words2.txt
  21. # ...
  22. # -rw------- martin/martin 42 2021-08-23 09:02:22 words5.txt

我们从归档文件的基本创建开始,这里使用的打开模式 "w:gz" ,指定要使用 GZ 压缩。然后将所有的文件添加到存档中。使用 tarfile 模块,还可以传入符号链接(软连接)、或传入可以递归添加的整个目录。

接下来,为了确认所有文件都确实存在,我们使用 getmembers 方法。为了深入了解各个文件,可以使用 gettarinfo 方法,它提供了所有 Linux 文件属性。

tarfile 提供了一个我们在其他模块中没有看到的很酷的特性,那就是在将文件添加到归档文件时能够修改文件的属性。在上面的代码片段中,通过提供 filter 参数来更改文件的权限,该参数修改了 TarInfo.mode。此值必须作为八进制数提供,此处的 0o100600 将权限设置为 0600 或 -rw-------.

为了在进行此更改后获得文件的完整概览,我们可以运行 list 方法,它提供类似于 ls -l的输出。

使用tar 存档的最后一件事是打开它并将其解压缩。为此,我们使用 "r:gz" 模式打开它,以文件名作为 getmember 方法的参数,返回文件对象,并将其解压缩到指定路径中。

  1. 1
  2. 2
  3. 3
  4. 4
  1. with tarfile.open(archive, "r:gz") as tar:
  2. member = tar.getmember("words3.txt")
  3. if member.isfile():
  4. tar.extract(member, "/tmp/")

结论

如你所见,Python 的提供了包括低级和高级、特定和通用、简单和复杂的各类模块或库。可以根据实际需要进行选择,通常建议使用通用模块,如 zipfile 或 tarfile ,只有在必要时才使用 lzma 之类的模块。

当然,要想熟练使用以上各个模块的各种方法,还是要阅读官方文档。

原文链接:https://blog.csdn.net/m0_63394128/article/details/125131301



所属网站分类: 技术文章 > 博客

作者:2322wewe

链接:https://www.pythonheidong.com/blog/article/1895636/66b12eace73bef8c37dd/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

18 0
收藏该文
已收藏

评论内容:(最多支持255个字符)