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
相关产品推荐
相关产品推荐

