我的任务是手动读取 7z 档案,而不使用 7z 实用程序。它们似乎是使用默认设置创建的,因此很可能版本 4 档案中的数据是使用 LZMA2 算法压缩的(在版本 3 档案中,显然是使用 LZMA1 算法),并且元数据在 LZMA1 中压缩(根据文档)。
使用科学戳方法,我设法从一些(但由于某种原因不是全部)档案中读取数据:
import lzma
with open('archive.7z', 'rb') as fp:
fp.seek(32) # Пропускаем 7z-заголовок
data = lzma.LZMAFile(fp, format=lzma.FORMAT_RAW, filters=[{"id": lzma.FILTER_LZMA2}]).read()
但是,我不知道如何读取元数据。(是否应用了任何特殊过滤器?)
你能告诉我它们是如何编码的以及需要为 lzma 解压缩器设置哪些参数才能读取元数据吗?

偶然发现了来自py7zr项目的非官方格式规范。它有点难以阅读,似乎有一些错误,但是这个规范,通过阅读py7zr 源代码,帮助我获得了元数据。
我不会将规范的一半复制粘贴到这个答案中,但我会注意到一些重要的,在我看来,要点:
该格式有一个特殊的类型 NUMBER - 这是一个可变长度的数字,其中数字的长度由第一个字节中的高位设置,并且必须记住所有 NUMBER 才能正确解码;
如果 End Header 以字节 0x17 开头,则元数据被编码(压缩或加密)并且 End Header 包含解密指令。如果以 0x01 开头,那么元数据直接写入 End Header 而不编码,但是这样的存档非常少见(我的收藏中只找到了一个);
格式中有文件夹、流、编码器和过滤器的复杂概念,它们也会影响元数据的编码并使 End Header 的解析复杂化。但是,显然,一个文件夹、一个流和一个 LZMA1 编码器总是用于没有特殊过滤器的元数据(但这并不准确);
一般来说,EncodedHeader 结构从 0x17 开始,并包含另外两个结构: PackInfo (0x06) - 有关编码数据的位置和大小的信息;和 UnpackInfo (0x07) - 有关设置编码器和过滤器的信息,以及解码数据的大小及其 CRC32 哈希值。
从 EncodedHeader 中读取所有这些信息后,我们可以减去压缩的元数据并成功解压:
将来,这些解压后的元数据也需要进行解析,以便与档案一起进行全面的工作,但这是一个完全不同的故事。
在我在问题图片中显示的示例存档中,End Header 的排列方式如下(点击放大):