golangは低レベルな処理(バイナリの取り扱いとかね)もちゃんと行うことができる.
基本(byte型を使う)
golangは組み込みでbyte型が用意されている.
これはその名の通りバイトを扱うための型で, 配列やスライスにすれば単純なバイトストリームを扱える.
package main
import (
"fmt"
)
func main() {
b := []byte{0xDe, 0xaD, 0xBe, 0xeF}
fmt.Printf("%b\n", b)
fmt.Printf("%d\n", b)
fmt.Printf("%x\n", b)
fmt.Printf("%X\n", b)
}
[実行結果]
[11011110 10101101 10111110 11101111]
[222 173 190 239]
deadbeef
DEADBEEF
bytesパッケージ
byteのスライスを扱う場面は多いので, 標準で操作するパッケージが用意されている.
(Package bytes)https://golang.org/pkg/bytes/
以下のように比較も簡単だし, 文字列操作を意識しているのかその手の関数が多い.
(byteのスライスはstringとしてみなせるし)
package main
import (
"fmt"
"bytes"
)
func main() {
b := []byte{0xDe, 0xaD, 0xBe, 0xeF}
b2 := []byte{0xDe, 0xaD, 0x00, 0x00}
b3 := []byte{0xDe, 0xaD, 0xBe, 0xeF}
c := bytes.Compare(b, b2)
fmt.Println(c)
c2 := bytes.Compare(b, b3)
fmt.Println(c2)
}
[実行結果]
1
0
ファイルに書いてみる
ファイルはただのバイト列なので, ファイルにも書けますね.
package main
import (
"io/ioutil"
)
func main() {
b := []byte{0xDe, 0xaD, 0xBe, 0xeF}
ioutil.WriteFile("test.bin", b, 0644)
}
[実行結果]
Endianの都合で逆になっちゃってますが...(Windowsはリトルエンディアン)
$ od -x test.bin
0000000 adde efbe
0000004
バッファを扱う(bytes.bufferを使う)
バイトストリームを操作する時にイチイチ操作関数を使っていると面倒でしょうがないです.
io.Readerやio.Writerの操作メソッドを使いたいですね.
あと, いきなりファイルに書いたりせずにバッファリングしたい場面がほとんどです.
bytesパッケージにはbufferという構造体が用意されています.
標準入出力のテストにも使えます.
以下の様なパッケージがあるとして
package hello
import (
"fmt"
"io"
)
func hello(out io.Writer) {
fmt.Fprintf(out, "hello, world")
}
上記のテストコードは以下になります.
bufferへ書き出して比較するという感じです.
helloの引数にinterfaceを使ったおかげでいい感じになります.
package hello
import (
"bytes"
"testing"
)
func TestHello(t *testing.T) {
expected := []byte("hello, world")
stdout := new(bytes.Buffer)
hello(stdout)
if bytes.Compare(stdout.Bytes(), expected) != 0 {
t.Fatal("greeting is not matched")
}
}
このテストは無事通ります.
[実行結果]
$ go test
PASS
ok _/C_/msys64/home/twinbird/test 0.128s
バイトストリームを構造体へ変換する(encoding/binaryを使う)
プログラムでデータを扱うとなるとやっぱり構造体でまとめたいですね.
一々バイトストリームから構造体へ変換する処理を書くのは面倒ですが,
golangにはいい感じにしてくれるパッケージがあります.
package main
import (
"bytes"
"fmt"
"encoding/binary"
)
type Beef struct {
One byte
Two byte
Three byte
Four byte
}
func main() {
b := []byte{0xDe, 0xaD, 0xBe, 0xeF}
buf := bytes.NewBuffer(b)
var beef Beef
binary.Read(buf, binary.LittleEndian, &beef)
fmt.Printf("%x\n", beef.One)
fmt.Printf("%x\n", beef.Two)
fmt.Printf("%x\n", beef.Three)
fmt.Printf("%x\n", beef.Four)
}
[実行結果]
$ ./test.exe
de
ad
be
ef
読み出しだけ書いてみましたが, 書き出しもあります.
注意点としては
- 構造体のメンバはちゃんとエクスポートしておかないとダメ
- 渡すのは構造体へのポインタ
というところくらいだと思います.
エンディアンも指定出来て良いです.