11.7 Go语言自定义二进制文件的读写操作 - Go语言中文社区

11.7 Go语言自定义二进制文件的读写操作


虽然Go语言的 encoding/gob 包非常易用,而且使用时所需代码量也非常少,但是我们仍有可能需要创建自定义的二进制格式。自定义的二进制格式有可能做到最紧凑的数据表示,并且读写速度可以非常快。

不过,在实际使用中,我们发现以Go语言二进制格式的读写通常比自定义格式要快非常多,而且创建的文件也不会大很多。但如果我们必须通过满足 gob.GobEncoder 和 gob.GobDecoder 接口来处理一些不可被 gob 编码的数据,这些优势就有可能会失去。

在有些情况下我们可能需要与一些使用自定义二进制格式的软件交互,因此了解如何处理二进制文件就非常有用。

写自定义二进制文件

Go语言的 encoding/binary 包中的 binary.Write() 函数使得以二进制格式写数据非常简单,函数原型如下:

  1. func Write(w io.Writer, order ByteOrder, data interface{}) error

Write 函数可以将参数 data 的 binary 编码格式写入参数 w 中,参数 data 必须是定长值、定长值的切片、定长值的指针。参数 order 指定写入数据的字节序,写入结构体时,名字中有_的字段会置为 0。

下面通过一个简单的示例程序来演示一下 Write 函数的使用,示例代码如下:

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "os"
  7. )
  8. type Website struct {
  9. Url int32
  10. }
  11. func main() {
  12. file, err := os.Create("output.bin")
  13. for i := 1; i <= 10; i++ {
  14. info := Website{
  15. int32(i),
  16. }
  17. if err != nil {
  18. fmt.Println("文件创建失败 ", err.Error())
  19. return
  20. }
  21. defer file.Close()
  22. var bin_buf bytes.Buffer
  23. binary.Write(&bin_buf, binary.LittleEndian, info)
  24. b := bin_buf.Bytes()
  25. _, err = file.Write(b)
  26. if err != nil {
  27. fmt.Println("编码失败", err.Error())
  28. return
  29. }
  30. }
  31. fmt.Println("编码成功")
  32. }

运行上面的程序会在当前目录下生成 output.bin 文件,文件内容如下:

  1. 0100 0000 0200 0000 0300 0000 0400 0000
  2. 0500 0000 0600 0000 0700 0000 0800 0000
  3. 0900 0000 0a00 0000

读自定义二进制文件

读取自定义的二进制数据与写自定义二进制数据一样简单。我们无需解析这类数据,只需使用与写数据时相同的字节顺序将数据读进相同类型的值中。

示例代码如下:

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "os"
  7. )
  8. type Website struct {
  9. Url int32
  10. }
  11. func main() {
  12. file, err := os.Open("output.bin")
  13. defer file.Close()
  14. if err != nil {
  15. fmt.Println("文件打开失败", err.Error())
  16. return
  17. }
  18. m := Website{}
  19. for i := 1; i <= 10; i++ {
  20. data := readNextBytes(file, 4)
  21. buffer := bytes.NewBuffer(data)
  22. err = binary.Read(buffer, binary.LittleEndian, &m)
  23. if err != nil {
  24. fmt.Println("二进制文件读取失败", err)
  25. return
  26. }
  27. fmt.Println("第", i, "个值为:", m)
  28. }
  29. }
  30. func readNextBytes(file *os.File, number int) []byte {
  31. bytes := make([]byte, number)
  32. _, err := file.Read(bytes)
  33. if err != nil {
  34. fmt.Println("解码失败", err)
  35. }
  36. return bytes
  37. }

运行结果如下:

  1. go run main.go
  2. 1 个值为: {1}
  3. 2 个值为: {2}
  4. 3 个值为: {3}
  5. 4 个值为: {4}
  6. 5 个值为: {5}
  7. 6 个值为: {6}
  8. 7 个值为: {7}
  9. 8 个值为: {8}
  10. 9 个值为: {9}
  11. 10 个值为: {10}

至此,我们完成了对自定义二进制数据的读和写操作。只要小心选择表示长度的整数符号和大小,并将该长度值写在变长值(如切片)的内容之前,那么使用二进制数据进行工作并不难。

Go语言对二进制文件的支持还包括随机访问。这种情况下,我们必须使用 os.OpenFile() 函数来打开文件(而非 os.Open()),并给它传入合理的权限标志和模式(例如 os.O_RDWR 表示可读写)参数。

然后,就可以使用 os.File.Seek() 方法来在文件中定位并读写,或者使用 os.File.ReadAt() 和 os.File.WriteAt() 方法来从特定的字节偏移中读取或者写入数据。

Go语言还提供了其他常用的方法,包括 os.File.Stat() 方法,它返回的 os.FileInfo 包含了文件大小、权限以及日期时间等细节信息。

版权声明:本教程内容除了本站原创内容外,还有来源自C语言编程网,博客园,CSDN等技术站点,感谢相关博主原创文章,转载请附上原文出处链接和本声明。
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 阅读 ( 612 )
  • 分类:Go

0 条评论

官方社群

GO教程

猜你喜欢