c++ 读文件_如何优雅的随机读 gzip 压缩文件

28de302b4035e11885a9f1b408e7f051.png

随机读文件是很简单的,在 Java 中使用 java.io.RandomAccessFile 就可以了:可以通过 seek(pos) 方法设置读位置,也可以通过 getFilePointer() 来获取读位置,还可以通过 skipBytes(n) 来实现单向的相对移动。

但是考虑一个场景,目标文件都非常巨大,而且都是文本文件,那么对这个文件进行压缩可以获得很高的压缩比(对于常见的日志文件,gzip 格式可以获得 7% ~ 10% 的压缩率)。但是这样就无法直接随机读写原始文件的指定位置的数据了,因为无法根据在原始数据的偏移量换算出对应在压缩数据中的偏移量。

为什么无法换算?

gzip 使用的 zlib 对数据进行压缩,zlib 使用的是 DEFLATE 算法,压缩过的数据被称为 Deflate Stream,它压缩的时候按照下面的过程来进行:

  1. 首先使用引用来替换重复出现的序列。如果一个序列在 32K 的范围内重复出现了若干次,那么从第二次开始,压缩过的数据中将会使用 23 个 bit 来引用第一次出现的位置(15bit 偏移量 + 8bit 长度),
  2. 对前面一段流中(称为块)出现的数据进行哈夫曼编码,使用更少的 bit 来表示更常出现的数据,从而降低块的整体大小

所以来说,对于不同的原始数据,替换重复序列后的结果和哈夫曼编码的结果会迥异,因此没有一个通用的方法能够仅仅根据原始数据中的位置来换算出压缩后的位置。

更重要的是,Deflate Stream 是比特流,原始数据中某个位置在压缩以后可能对应到非字节边界上,这是无论如何也无法通过直接 seek 来定位的。

既然无法直接换算和定位,那么有没有别的办法来实现直接根据原始数据的偏移量来直接读取数据呢?

大力出奇迹

大力出奇迹的代表是 zlib 官方提供的 gzseek() / gzrewind() 方法:

ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, z_off_t offset, int whence));
Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported.

但它是怎么实现的呢?简单来说,就是从文件头部开始一边解压缩一边吭哧吭哧计数,直至到达指定的位置为止

功能是实现了,不过也挺简单粗暴的。关键是性能堪忧啊,假如是要读最后几个字节的数据,它得把整个文件给你全部解压缩一遍。严格意义上来说,这就不叫随机读。

人家倒也实诚,文档中也说了:

If the file is opened for reading, this function is emulated but can be extremely slow.

空间换时间:zran.c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值