什么是最好的压缩算法,可以在文件中进行随机读写?

31

什么是最好的压缩算法,允许在文件中进行随机读/写操作?

我知道任何自适应压缩算法都不合适。

我也知道霍夫曼编码不合适。

有没有更好的压缩算法可以允许随机读/写操作呢?

如果你将任何压缩算法分成块写入,我认为你可以使用它,但理想情况下,我不想一次解压整个块。但是,如果您对如何轻松实现此操作以及如何确定块边界有建议,请告诉我。如果这是您解决问题的一部分,请还要告诉我当您要读取的数据跨越块边界时该怎么办?

请在回答中假设所讨论的文件大小为100GB,有时我会想读取前10个字节,有时我会想读取最后19个字节,有时我会想读取中间的17个字节。

5个回答

29

我对许多回复认为这是不可能的数量感到惊讶。

这些人从未听说过“压缩文件系统”吗?早在1993年微软就因压缩文件系统技术被Stac Electronics起诉之前,这项技术就已经存在了。

我听说LZSLZJB是流行的算法,用于实现压缩文件系统,这些系统必须同时支持随机读取和随机写入。

也许最简单最好的方法就是为该文件打开文件系统压缩功能,让操作系统处理细节。但如果您坚持手动处理它,也许可以通过阅读有关NTFS透明文件压缩的文章学习一些技巧。

还可以查看:“StackOverflow:哪些压缩格式支持归档内的随机访问?”


1
打开文件系统压缩是一个很好的解决方案。 - Derek Tomes
2
如果你读了那些回答中说“不可能”的,我认为你会发现争议的问题在于术语。大家都同意,你可以有一个文件格式,在这个格式中,如果你想要第10000个字节,你可以找到包含该字节的块,并只读取那个块,直到你得到第10000个字节。并非每个人都认为这是“随机访问”,这正是问题所指定的。 - afeldspar
4
按照这种愚蠢的逻辑,"随机访问"根本不存在,因为你无法在不读取其周围4k块的情况下读取1字节。更不用说你无法仅仅读取1个比特而非整个字节。 - Navin
@DerekTomes 尝试在 NTFS 上进行此操作,发现在大量随机读/写期间出现了虚假故障,无论是在文件同步还是执行写操作时(很难复制),减速约为 15 倍。 - AlexO

3

我认为Stephen Denne可能有所发现。想象一下:

  • 将序列压缩成代码
  • 一个将代码映射到序列的字典
  • 文件就像一个文件系统
    • 每次写入都会生成一个新的“文件”(一系列按照字典压缩的字节)
    • “文件系统”跟踪哪个“文件”属于哪些字节(起始、结束位置)
    • 每个“文件”都按照字典进行压缩
    • 读取按文件方式工作,根据“文件系统”解压缩和检索字节
    • 写入会使“文件”无效,新的“文件”会被追加以替换失效的“文件”
  • 这个系统需要:
    • 文件系统的碎片整理机制
    • 定期压缩字典(删除未使用的代码)
  • 如果做得好,可以在无人注意(空闲时间)或创建新文件并最终“切换”时进行清理。

一个积极的影响是字典将适用于整个文件。如果你可以节省CPU周期,你可以定期检查重叠“文件”边界的序列,然后重新分组它们。

这个想法是为了真正随机的读取。如果你只会读取固定大小的记录,这个想法的某些部分可能会更容易。


3

使用基于字典的压缩方案,每个字典条目的代码都以相同的大小编码,这将使得可以在任何代码大小的倍数处开始阅读,并且如果代码不使用其上下文/邻居,则编写和更新很容易。

如果编码包括区分代码开头或结尾的方法,则不需要代码具有相同的长度,您可以从文件中间的任何位置开始阅读。如果您正在从流的未知位置进行阅读,则此技术更有用。


1

我不知道有哪种压缩算法允许随机读取,更不用说随机写入了。如果您需要这种能力,最好的选择是将文件分块压缩而不是整体压缩。

例如:
首先我们来看只读情况。假设您将文件分成8K块。您压缩每个块并按顺序存储每个压缩块。您需要记录每个压缩块存储在哪里以及它的大小。然后,假设您需要从偏移量O开始读取N个字节。您需要确定它在哪个块中(O / 8K),解压该块并获取这些字节。您需要的数据可能跨越多个块,因此您必须处理这种情况。

当您想要能够写入压缩文件时,事情就变得复杂了。您必须处理压缩块变大和变小的情况。您可能需要为每个块添加一些额外的填充以防它扩展(虽然未压缩大小相同,但不同的数据将压缩为不同的大小)。如果压缩数据太大无法放回原始空间,则甚至可能需要移动块。

这基本上就是压缩文件系统的工作原理。您最好打开文件系统压缩功能,然后正常读写文件。


1
我曾经发布过一篇关于哈夫曼编码的答案。阅读了你的回复让我停下来思考哈夫曼编码是如何完成的,你说得对,随机写入会破坏编码。 - Bill the Lizard
在写入的情况下,您永远不需要额外的填充。您只需要重新压缩共享越过边界的两个块即可。这是因为没有API会将数据插入文件的位置。 - Brian R. Bondy
@Brian R. Bondy:当然,写入操作比这更糟糕,因为它们可以改变压缩文件的大小(即使未压缩的数据保持相同大小)。 - Hugh Allen
@Brian - 我在想你可以将该块重新映射到文件中的其他位置。 - Ferruccio

1

压缩是从数据中删除冗余的过程。不幸的是,冗余很可能不会均匀地分布在整个文件中,这是唯一可以期望同时获得压缩和细粒度随机访问的情况。

然而,您可以通过在压缩过程中维护一个外部列表来接近随机访问,该列表显示未压缩数据流中选择点与其在压缩数据流中的位置之间的对应关系。显然,您必须选择一种方法,其中源流和其压缩版本之间的转换方案不会随着流中的位置而变化(即没有LZ77或LZ78;相反,您可能希望选择Huffman或字节对编码)。显然,这将产生很多开销,并且您必须决定如何在存储“书签点”所需的存储空间和解压缩从书签点开始以获取实际查找的数据所需的处理器时间之间进行权衡。

至于随机访问式写入...几乎是不可能的。 正如已经注意到的那样,压缩是关于从数据中消除冗余的。如果您尝试使用没有相同冗余的数据替换因为冗余而被压缩的数据,则它根本不适合。

然而,根据您要进行多少随机访问式写入--您可以通过维护代表压缩后写入文件的所有数据的稀疏矩阵来模拟它。在所有读取时,您将检查矩阵是否正在读取您在压缩后写入的区域。 如果没有,则您将转到压缩文件以获取数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接