Golang bytes 模块

2017-06-08 language golang

Golang 中的 bytes 包使用频率较高,主要提供了字节相关的操作,通常作为缓冲区使用。

简介

String 是固定的一系列字节,创建后不能修改;而 Bytes 可动态调整,如果要修改字符串的内容,需要转换为 Bytes 。

两者之间可以相互转换,例如:

s := "abc"
b := []byte(s)
s := string(b)

在两者相互转换时,会先完成内存申请,然后将对应的内容进行拷贝。

package main

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

func IntToBytes(n int) []byte {
        bytesBuffer := bytes.NewBuffer([]byte{})
        binary.Write(bytesBuffer, binary.BigEndian, int32(n))
        return bytesBuffer.Bytes()
}

func main() {
        var b0 bytes.Buffer
        b0.Write([]byte("Hello "))
        fmt.Println(b0.String())

        b1 := new(bytes.Buffer)
        b1.WriteString("Hi World")
        b1.WriteByte('!')
        fmt.Println(b1.String())

/*
	// OR
	b2 := bytes.NewBufferString("swift")
	b3 := bytes.NewBuffer([]byte("swift"))
	b4 := bytes.NewBuffer([]byte{'s', 'w', 'i', 'f', 't'})

	// Empty Buffer
	b5 = bytes.NewBufferString("")
	b6 = bytes.NewBuffer([]byte{})
*/
}

bytes

bytes.Buffer 是一个字节类型的缓冲器,可变长,实际上内部指向的是一个 Slice 对象。

如下是一些常见的操作。

//----- 分配对象可以动态扩容,如果大小是固定可以指定打消
var b bytes.Buffer
b := new(bytes.Buffer)
b := bytes.NewBufferString("hello")
b := bytes.NewBuffer([]byte("hello"))

b.Write([]byte("Hello"))
b.WriteString("Hello")

b.Bytes() // 获取对应的Slice

在该包中,提供了一个如下的结构体。

type Buffer struct {
	buf       []byte            // contents are the bytes buf[off : len(buf)]
	off       int               // read at &buf[off], write at &buf[len(buf)]
	bootstrap [64]byte          // memory to hold first slice; helps small buffers avoid allocation.
	lastRead  readOp            // last read operation, so that Unread* can work correctly.
}

数据会保存在 buf []byte 对象中,那么无非就是当内存空间不足的时候如何进行扩容。

序列化

二进制文件读写方式,可以通过标准库提供的 encoding/binary 库。

package main

import (
        "bytes"
        "encoding/binary"
        "log"
        "os"
        "unsafe"
)

const (
        T_WRITE = 0x1234
)

type Package struct {
        Magic  [4]byte
        Type   uint16
        Length uint32
}

func main() {
        file, err := os.OpenFile("foobar.bin", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
        if err != nil {
                log.Fatal(err)
        }
        defer file.Close()

        // Write
        data := &Package{
                Magic:  [4]byte{'A', 'B', 'C', 'D'},
                Type:   T_WRITE,
                Length: 10,
        }

        buff := new(bytes.Buffer)
        binary.Write(buff, binary.LittleEndian, data)

        nwritten, err := file.Write(buff.Bytes())
        if err != nil {
                log.Fatal(err)
        }
        log.Printf("Write %d bytes\n", nwritten)

        // Read
        if _, err := file.Seek(0, 0); err != nil {
                log.Fatal(err)
        }
        rdata := make([]byte, unsafe.Sizeof(Package{}))

        pkg := Package{}
        nread, err := file.Read(rdata)
        if err != nil {
                log.Fatal(err)
        }
        log.Printf("Read %d bytes\n", nread)
        binary.Read(bytes.NewBuffer(rdata), binary.LittleEndian, &pkg)
        log.Printf("Got data %v\n", pkg)
}

如果是多个二进制文件,那么就可以通过 binary.LittleEndian.Uint16 进行转换。