首页 > 编程笔记 > Go语言笔记 阅读:14

Go语言读写二进制文件(附带实例)

Go 语言的二进制格式是一个自描述的二进制序列。从其内部表示来看,Go 语言的二进制格式由一个 0 块或更多块的序列组成,其中每块都包含一个字节数,一个由 0 个或多个 typeId-typeSpecification 对(即“类型对”)组成的序列,以及一个 typeId-value 对(即“值对”)。

如果 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
语法说明如下:
下面通过几个实例演示如何对自定义格式的二进制文件执行写入、读取操作。

【实例 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}

相关文章