You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言int数组持久化后恢复遇EOF错误排查求助

解决gzip解压时的EOF错误及数组持久化的效率疑问

一、为什么会出现EOF错误?

你代码里的核心问题出在gzip.Writer的关闭时机上。你用了defer compressor.Close(),但defer是在当前函数(main)执行完毕后才会触发的。而你在调用compressor.Write(indexBuffer.Bytes())之后,立刻就去读取compressionBuffer.Bytes(),这时候gzip的内部缓冲区还没把所有数据(包括gzip的结束标记)写入到compressionBuffer里,导致解压时拿到的是不完整的压缩数据,自然就会报EOF错误。

另外还有个小细节:你用了bufio.Writer但没调用Flush,虽然小数据量下可能没问题,但数据量大时bufio的缓冲区会残留未写入的数据,也会导致后续处理出错。

修正后的代码

package main

import (
    "bufio"
    "bytes"
    "compress/gzip"
    "encoding/gob"
    "fmt"
)

func main() {
    arry := []int{1, 2, 3, 4, 5}
    // Gob编码
    var indexBuffer bytes.Buffer
    writer := bufio.NewWriter(&indexBuffer)
    encoder := gob.NewEncoder(writer)
    if err := encoder.Encode(arry); err != nil {
        panic(err)
    }
    // 必须先flush bufio.Writer,确保所有gob数据写入indexBuffer
    writer.Flush()

    // 压缩数据
    var compressionBuffer bytes.Buffer
    compressor := gzip.NewWriter(&compressionBuffer)
    _, err := compressor.Write(indexBuffer.Bytes())
    if err != nil {
        panic(err)
    }
    // 关键:手动关闭compressor,确保压缩数据全部写入缓冲区(包括结束标记)
    if err := compressor.Close(); err != nil {
        panic(err)
    }

    // 解压数据
    buf := bytes.NewBuffer(compressionBuffer.Bytes())
    reader, err := gzip.NewReader(buf)
    if err != nil {
        fmt.Println("gzip failed ", err)
        panic(err)
    }
    defer reader.Close()

    // Gob解码
    var intArray []int
    decoder := gob.NewDecoder(reader)
    if err := decoder.Decode(&intArray); err != nil {
        fmt.Println("gob failed ", err)
        panic(err)
    }
    fmt.Println("final int Array content: ", intArray)
}

二、关于gob的必要性和存储效率的疑问

1. 是否必须使用gob?

完全不是必须的。gob是Go专属的序列化格式,它的优势是无需手动处理类型细节,能自动支持几乎所有Go类型(包括自定义结构体),还会自动处理字节序问题(跨平台时更省心)。但如果只是简单的int数组,你有更轻量的选择。

2. 直接存储byte数组是否比gob更高效?

是的,直接把int数组转换成byte数组存储会比gob更高效,原因有两点:

  • 体积更小:gob会在序列化时写入额外的类型元数据(比如标记这是一个int切片、元素类型等),而直接转byte数组只存储每个int的原始字节,没有额外开销。
  • 速度更快:省去了gob的类型解析和编码/解码开销,直接操作原始字节的速度更快。

替代方案:用encoding/binary直接序列化int数组

如果追求极致效率,可以用encoding/binary包直接把int数组转成byte数组,示例代码如下:

package main

import (
    "bytes"
    "compress/gzip"
    "encoding/binary"
    "fmt"
)

func main() {
    arry := []int64{1, 2, 3, 4, 5} // 用int64确保跨平台字节大小一致(固定8字节)
    // 直接将int数组转成byte数组
    var byteBuffer bytes.Buffer
    for _, num := range arry {
        // 指定字节序(大端序,跨平台统一)
        err := binary.Write(&byteBuffer, binary.BigEndian, num)
        if err != nil {
            panic(err)
        }
    }

    // 压缩
    var compressionBuffer bytes.Buffer
    compressor := gzip.NewWriter(&compressionBuffer)
    _, err := compressor.Write(byteBuffer.Bytes())
    if err != nil {
        panic(err)
    }
    compressor.Close()

    // 解压
    buf := bytes.NewBuffer(compressionBuffer.Bytes())
    reader, err := gzip.NewReader(buf)
    if err != nil {
        panic(err)
    }
    defer reader.Close()

    // 读取解压后的byte数组
    decompressedBytes := make([]byte, 0)
    temp := make([]byte, 1024)
    for {
        n, err := reader.Read(temp)
        if err != nil {
            break
        }
        decompressedBytes = append(decompressedBytes, temp[:n]...)
    }

    // 转成int数组
    intArray := make([]int64, 0, len(decompressedBytes)/8)
    for i := 0; i < len(decompressedBytes); i += 8 {
        var num int64
        binary.Read(bytes.NewReader(decompressedBytes[i:i+8]), binary.BigEndian, &num)
        intArray = append(intArray, num)
    }
    fmt.Println("final int Array content: ", intArray)
}

这里用int64代替int是为了确保跨平台时字节大小一致,避免32位/64位系统的兼容性问题。


内容的提问来源于stack exchange,提问作者amlwwalker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:12:09