Go语言读写二进制文件(附带实例)
Go 语言的二进制格式是一个自描述的二进制序列。从其内部表示来看,Go 语言的二进制格式由一个 0 块或更多块的序列组成,其中每块都包含一个字节数,一个由 0 个或多个 typeId-typeSpecification 对(即“类型对”)组成的序列,以及一个 typeId-value 对(即“值对”)。
如果 typeId-value 对的 typeId 是预先定义好的(如 bool、int 和 string 等),则这些 typeId-typeSpecification 对可以省略。否则就要用类型对描述一个自定义类型(如一个自定义的结构体)。类型对和值对之间的 typeId 没有区别。
类型对和值对源于 C++ 语言的类型和值。对此感兴趣的读者可自行查阅相关资料。
下面通过几个实例演示如何对 gob 格式的二进制文件执行写入、读取操作。
【实例 1】对 gob 格式的二进制文件执行写入操作。在当前程序所在的目录下,创建名为 output.gob 的二进制 gob 格式文件,把诗句“黄沙百战穿金甲,不破楼兰终不还。”写入 output.gob。代码如下:

图 1 对 gob 格式的二进制文件执行写入操作
【实例 2】对 gob 格式的二进制文件执行读取操作。读取名为 output.gob 的二进制 gob 格式文件,把 output.gob 中的数据打印在控制台上。代码如下:
在开发过程中,以 Go 语言 gob 格式的读写通常比自定义二进制格式快很多,而且创建的文件也不大。但是,当必须使用 gob.GobEncoder 和 gob.GobDecoder 接口处理一些不可被 gob 格式编码的数据时,需要自定义二进制格式。
使用 encoding/binary 包的 binary.Write() 函数以二进制格式写数据非常简单,其语法格式如下:
下面通过几个实例演示如何对自定义格式的二进制文件执行写入、读取操作。
【实例 3】对自定义格式的二进制文件执行写入操作。在当前程序所在的目录下,创建名为 output.bin 的自定义格式二进制文件,把数字 1~10 依次写入 output.bin 中。代码如下:

图 2 对自定义格式的二进制文件执行写入操作
【实例 4】对自定义格式的二进制文件执行读取操作。对自定义格式二进制文件 output.bin 执行读取操作,把 output.bin 中的数据打印在控制台上。代码如下:
如果 typeId-value 对的 typeId 是预先定义好的(如 bool、int 和 string 等),则这些 typeId-typeSpecification 对可以省略。否则就要用类型对描述一个自定义类型(如一个自定义的结构体)。类型对和值对之间的 typeId 没有区别。
类型对和值对源于 C++ 语言的类型和值。对此感兴趣的读者可自行查阅相关资料。
gob格式
Go 语言中的 encoding/gob 包也提供了与 encoding/json 包一样的编码、解码功能。通常而言,如果对文件的可读性没有要求,gob 格式是 Go 语言中文件存储和网络传输最方便的格式。下面通过几个实例演示如何对 gob 格式的二进制文件执行写入、读取操作。
【实例 1】对 gob 格式的二进制文件执行写入操作。在当前程序所在的目录下,创建名为 output.gob 的二进制 gob 格式文件,把诗句“黄沙百战穿金甲,不破楼兰终不还。”写入 output.gob。代码如下:
package main import ( "encoding/gob" "fmt" "os" ) func main() { // 待编码的诗句 info := "黄沙百战穿金甲,不破楼兰终不还。" // 创建输出文件 file, err := os.Create("output.gob") if err != nil { fmt.Println("文件创建失败", err.Error()) return } defer file.Close() // 创建 gob 编码器 encoder := gob.NewEncoder(file) // 编码并写入 err = encoder.Encode(info) if err != nil { fmt.Println("编码错误", err.Error()) return } // 编码成功 fmt.Println("编码成功") }运行程序后,当控制台打印“编码成功”时,说明在当前程序所在的目录下,已经创建 output.gob,如下图所示:

图 1 对 gob 格式的二进制文件执行写入操作
【实例 2】对 gob 格式的二进制文件执行读取操作。读取名为 output.gob 的二进制 gob 格式文件,把 output.gob 中的数据打印在控制台上。代码如下:
package main import ( "encoding/gob" // 原图左引号错误,已修正 "fmt" "os" ) func main() { // 打开 gob 文件 file, err := os.Open("output.gob") if err != nil { fmt.Println("文件打开失败", err.Error()) return } defer file.Close() // 创建 gob 解码器 decoder := gob.NewDecoder(file) // 定义变量用于接收解码结果 var info string // 解码 err = decoder.Decode(&info) if err != nil { fmt.Println("解码失败", err.Error()) } else { fmt.Println("解码成功") fmt.Println(info) } }运行结果如下:
解码成功
黄沙百战穿金甲,不破楼兰终不还。
自定义二进制格式
Go 语言的 encoding/gob 包非常易用,并且使用时所需代码量也非常少,但有时仍需要创建自定义的二进制格式。自定义的二进制格式的数据表示可以更紧凑,并且读写速度非常快。在开发过程中,以 Go 语言 gob 格式的读写通常比自定义二进制格式快很多,而且创建的文件也不大。但是,当必须使用 gob.GobEncoder 和 gob.GobDecoder 接口处理一些不可被 gob 格式编码的数据时,需要自定义二进制格式。
使用 encoding/binary 包的 binary.Write() 函数以二进制格式写数据非常简单,其语法格式如下:
func Write(w io.Writer, order ByteOrder, data interface{}) error语法说明如下:
- Write() 函数把参数 data 的 binary 编码格式写入参数 w;
- 参数 data 必须是定长值、定长值的切片、定长值的指针;
- 参数 order 指定写入数据的字节序,当写入结构体时,名字中有 _ 的字段会置为 0。
下面通过几个实例演示如何对自定义格式的二进制文件执行写入、读取操作。
【实例 3】对自定义格式的二进制文件执行写入操作。在当前程序所在的目录下,创建名为 output.bin 的自定义格式二进制文件,把数字 1~10 依次写入 output.bin 中。代码如下:
package main import ( "bytes" "encoding/binary" "fmt" "os" ) // Website 结构体定义 type Website struct { Url int32 } func main() { // 创建输出文件 file, err := os.Create("output.bin") if err != nil { fmt.Println("文件创建失败", err.Error()) return } defer file.Close() // 循环写入 10 条记录 for i := 1; i <= 10; i++ { info := Website{ Url: int32(i), } // 将结构体编码为二进制 var binBuf bytes.Buffer err := binary.Write(&binBuf, binary.LittleEndian, info) if err != nil { fmt.Println("编码失败", err.Error()) return } // 写入文件 _, err = file.Write(binBuf.Bytes()) if err != nil { fmt.Println("写入失败", err.Error()) return } } fmt.Println("编码成功") }运行程序后,当控制台打印“编码成功”时,说明在当前程序所在的目录下创建 output.bin,如下图所示。

图 2 对自定义格式的二进制文件执行写入操作
【实例 4】对自定义格式的二进制文件执行读取操作。对自定义格式二进制文件 output.bin 执行读取操作,把 output.bin 中的数据打印在控制台上。代码如下:
package main import ( "bytes" "encoding/binary" "fmt" "os" ) // Website 结构体定义 type Website struct { Url int32 } func main() { // 打开二进制文件 file, err := os.Open("output.bin") if err != nil { fmt.Println("文件打开失败", err.Error()) return } defer file.Close() m := Website{} // 循环读取 10 条记录 for i := 1; i <= 10; i++ { data := readNextBytes(file, 4) // 读取 4 字节 buffer := bytes.NewBuffer(data) // 构造缓冲区 err := binary.Read(buffer, binary.LittleEndian, &m) if err != nil { fmt.Println("二进制文件读取失败", err) return } fmt.Println("第", i, "个值为:", m) } } // readNextBytes 从文件中读取指定字节数 func readNextBytes(file *os.File, number int) []byte { bytes := make([]byte, number) _, err := file.Read(bytes) if err != nil { fmt.Println("解码失败", err) } return bytes }运行结果如下:
第 1 个值为:{1}
第 2 个值为:{2}
第 3 个值为:{3}
第 4 个值为:{4}
第 5 个值为:{5}
第 6 个值为:{6}
第 7 个值为:{7}
第 8 个值为:{8}
第 9 个值为:{9}
第 10 个值为:{10}