Goでバイナリを固定長で読んでいくときのメモ
バイナリの先頭nバイトを読みたいときというのがあると思う。先頭4バイトを読むとデータのサイズが入ってて、その後nバイトがデータとか、そういうやつ。固定長で読む良い方法があんまりなくて(あったらおしえてくれ)、自分でsliceを所望のサイズでmakeして、そこにio.Readする。ちょっとはまったのでメモ。
[code lang=”ruby”] file, _ := os.Open(“test.bin”)
// 例1 buf := make([]byte, 256) read_size, _ := file.Read(buf) fmt.Println(read_size, “bytes read.”, string(buf))
// 例2 buf2 := make([]byte, 256) read_size2, _ := io.ReadFull(file, buf2) fmt.Println(read_size2, “bytes read.”, string(buf2)) [/code]
何も考えずにやると上の例1みたいになると思うが、これだとバッファが埋まるまで読んでくれないことがあってはまる(read_sizeが256にならない場合がある)。
そもそも、io.Readは1回呼ばれたときの読み出しサイズが保証されていないので、io.Readで読み出されるサイズはなんかよくわからないけど読み出し元の種類とか状況によるっぽい。数バイトくらいなら問題ないことが多いが、サイズが大きくなってくると1回のio.Readでバッファを埋められないことがでてくる。なので、io.ReadFullを使う(例2)。これならバッファが埋まるまでio.Readを繰り返し呼んでくれる。テキストファイルの読み込みとかだとio.Readを直接使うことはほとんどないが、バイナリを扱うときもできるだけ使わないのがよさそう。
ちなみに[]byteをstringにcastできるらしい。ASCII専用だろうが。
あと、2/4/8バイトを読んで数値として解釈するのはもっと簡単で、encoding/binaryをimportして、
[code lang=”ruby”] var num uint16 binary.Read(file, binary.LittleEndian, &num) [/code]
とすればnumの型の分(この場合は2バイト)だけ読み出してreaderを進めてくれる。楽。
追記(2016/04/16):
encoding/binary/binary.goを読むと、io.ReadFullを使っていることがわかる。なるほど。
あと、binary.Readは各整数型以外にもそれぞれのスライスに対応しているらしい。
[code lang=”ruby”] num := make([]uint16, 3) binary.Read(file, binary.LittleEndian, &num) fmt.Println(num) // => [14648 24880 25442] [/code]