Go でバイナリをゴリゴリ処理するときに覚えておくと良さそうなもの。
進数表示
fmt.Printf("%b", 10) // 1010
fmt.Printf("%d", 10) // 10
fmt.Printf("%x", 10) // a
fmt.Printf("%X", 10) // A
byte
byte 自体は uint8 のエイリアス
オクテットストリームは []byte が基本
b := []byte{0x01, 0x02, 0x03}
単なるスライスなので、 Array を直で触るのでなければ、
これが一番低レベルな表現と言える。
bytes
bytes は []byte を操作するための便利メソッドを提供する。
bytes.Equal([]byte{1,2,3}, []byte{4,5}) // false
ちなみに、バイトを文字とみなせば string なので、
bytes の関数は strings とインタフェースが似ている。
bytes.Buffer
bytes に含まれるが、 []byte をラップして Read(), Write() などを付けるもの。
つまり Buffer にすれば io.ReadWriter を満たすので、
io.ReadWriter を引数にするライブラリなどで使える。(ioutil / bufio etc)
func main() {
buf := bytes.NewBuffer([]byte{1, 2, 3})
buf.Write([]byte{4, 5, 6})
b := make([]byte, 3)
buf.Read(b) // 1, 2, 3
log.Println(b, buf.Bytes()) // 4, 5, 6
}
一旦読んでから、読んだ分を戻すことができる UnreadByte() と
中の []byte が取り出せる Bytes() を覚えとくと良い。
bytes.Reader
Buffer と違い readonly
io.Reader と io.Seeker etc があり、 Read(), Seek() ができる。
encoding/binary
[]byte と固定長型の変数(unit64 とか) 間でエンコード/デコードできる。
エンディアンも指定できる。
ネットワークプロトコルのパーサとか書く場合は必須。
例えば、 4byte づつ key, val を表すプロトコルがあった場合。
type Row struct {
Key uint16
Val uint16
}
func (r Row) String() string {
return fmt.Sprintf("(%s: %v)", string(r.Key), r.Val)
}
func main() {
buf := bytes.NewBuffer([]byte{0x0, 97, 0x3, 0x1})
row := Row{}
binary.Read(buf, binary.BigEndian, &row.Key)
binary.Read(buf, binary.BigEndian, &row.Val)
log.Println(row)
}