在Golang中读取Zlib压缩文件的最有效方法?

时间:2014-12-16 12:38:50

标签: go zlib

我正在读取并同时解析(解码)自定义格式的文件,该格式使用zlib压缩。我的问题是如何有效地解压缩然后解析未压缩的内容而不增加切片?我想解析它,同时将其读入可重用的缓冲区。

这适用于对速度敏感的应用程序,因此我希望尽可能高效地阅读它。通常我会ioutil.ReadAll然后再循环遍历数据来解析它。这次我想在阅读时解析它,而不必增加读取的缓冲区,以获得最大效率。

基本上我认为如果我能找到一个完美大小的缓冲区,那么我可以读入它,解析它,然后再次写入缓冲区,然后解析它等等。这里的问题是zlib每次调用Read(b)时,阅读器似乎都会读取任意数量的字节;它没有填补切片。因此,我不知道完美的缓冲区大小是多少。我担心它可能会破坏我写入两个块的一些数据,因此很难解析,因为有人说uint64可以从两个读取中分离出来,因此不会出现在相同的缓冲区读取中 - 或者可能是永远不会发生,并且它总是以与最初编写的相同大小的块读出来?

  1. 什么是最佳缓冲区大小,或者有计算方法吗?
  2. 如果我用f.Write(b []byte)将数据写入zlib编写器,则在读回压缩数据时可能会将这些相同的数据拆分为两个读数(意味着我必须在解析过程中获得历史记录),或者它会一直回到同一个阅读中吗?

2 个答案:

答案 0 :(得分:0)

您可以将zlib阅读器包装在bufio阅读器中,然后在顶部实现专用阅读器,通过从bufio阅读器读取来重建您的数据块,直到读取完整的块。请注意,bufio.Read在底层Reader上最多调用一次,因此您需要在循环中调用ReadByte。但是,bufio将为您处理zlib阅读器返回的不可预测的数据大小。

如果您不想实现专门的阅读器,您可以使用bufio阅读器并使用ReadByte()根据需要读取尽可能多的字节以填充给定的数据类型。最佳缓冲区大小至少是您最大的数据结构的大小,直到您可以进入内存的任何内容。

如果直接从zlib阅读器上阅读,则无法保证您的数据不会在两次读取之间分开。

另一种可能更清晰的解决方案是为您的数据实现一个编写器,然后使用io.Copy(your_writer,zlib_reader)。

答案 1 :(得分:0)

好的,所以我最终使用我自己的读者实现来解决这个问题。

基本上结构看起来像这样:

type reader struct {
 at int
 n int
 f io.ReadCloser
 buf []byte
}

这可以附在zlib阅读器上:

// Open file for reading
fi, err := os.Open(filename)
if err != nil {
    return nil, err
}
defer fi.Close()
// Attach zlib reader
r := new(reader)
r.buf = make([]byte, 2048)
r.f, err = zlib.NewReader(fi)
if err != nil {
    return nil, err
}
defer r.f.Close()

然后可以使用如下函数直接从zlib阅读器读取x个字节:

mydata := r.readx(10)

func (r *reader) readx(x int) []byte {
    for r.n < x {
        copy(r.buf, r.buf[r.at:r.at+r.n])
        r.at = 0
        m, err := r.f.Read(r.buf[r.n:])
        if err != nil {
            panic(err)
        }
        r.n += m
    }
    tmp := make([]byte, x)
    copy(tmp, r.buf[r.at:r.at+x]) // must be copied to avoid memory leak
    r.at += x
    r.n -= x
    return tmp
}

请注意,我无需检查EOF,因为我的解析器应该在正确的位置停止。