“逆向压缩比”并非go标准术语,实为解压后大小÷压缩前大小,即压缩比的倒数;gzip.reader无法预知原始大小,需在解压时用countwriter实时统计字节数计算。

什么是“逆向压缩比”?先说清楚概念
Go 里没有叫 inverse compression ratio 的标准术语。你实际想算的,是「解压后大小 ÷ 压缩前大小」——也就是原始数据被放大了多少倍(通常 1)。这其实是压缩比的倒数,不是什么新指标,只是分子分母调换了位置。
gzip.Reader 解压时无法直接拿到原始大小
标准库 compress/gzip 的 gzip.Reader 是流式解压器,它不解析或暴露 GZIP 文件头里的原始长度字段(OS 和 extra 字段中也不存这个值)。RFC 1952 明确规定:GZIP 格式**不强制存储原始未压缩大小**,只保证校验和(CRC32)和压缩后长度。
- 常见错误是读完
gzip.Reader后用io.Copy(ioutil.Discard, ...)统计字节数,再除以源文件大小——这可行,但属于“解压后测”,不是“解压前预估” - 如果你手头只有 .gz 文件且没保留原文件,
os.Stat("file.gz").Size()无法反推原始大小 - 某些工具(如
gunzip -l)会尝试解压头部几个块来估算,但 Go 标准库不提供该能力
可靠计算方式:解压过程中实时计数
最稳的办法是在解压数据流的同时累计写入字节数,而不是依赖元信息。这样既准确,又不增加额外 I/O。
- 用
io.TeeReader包装gzip.Reader,把解压出的每个字节都喂给一个计数器 - 或者更直接:用
io.Copy写入一个带计数的io.Writer(比如自定义的countWriter) - 别用
bytes.Buffer.Len()之后再取.Bytes(),那会多一次内存拷贝;计数器本身只存int64
type countWriter struct{ n int64 }
func (w *countWriter) Write(p []byte) (int, error) {
w.n += int64(len(p))
return len(p), nil
}
// 使用示例:
gr, _ := gzip.NewReader(f)
defer gr.Close()
cw := &countWriter{}
io.Copy(cw, gr) // cw.n 就是解压后总字节数
fmt.Printf("逆向压缩比: %.3f", float64(cw.n)/float64(srcSize))
注意 ZIP 和 TAR 场景下的混淆点
如果你处理的是 archive/zip 或 archive/tar,事情更复杂:这些是归档格式,不是压缩格式。一个 .zip 文件里可能混着未压缩、Deflate、ZSTD 等多种编码的文件条目;.tar.gz 是两层封装(TAR 打包 + GZIP 压缩),解压后得到的是 TAR 流,还得再过一遍 tar.Reader 才能逐个读出文件内容并累加大小。
- 别把
zip.File.FileHeader.UncompressedSize64当成整个归档的原始大小——它只是单个文件的 - 对
.tar.gz,必须先解压再解包,不能跳过中间步骤去“猜”总大小 - 没有通用函数能一键返回 “归档解压后的总逆向压缩比”,因为归档结构本身就不承诺线性映射
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











