2013年5月19日日曜日

Go言語 バイナリファイルを扱う

こんにちは。
svarvizです。

先日ついにGo1.1がリリースされましたね!
リリースノートは以下になります。
http://golang.org/doc/go1.1
Go1.1は下記から取得できます。
https://code.google.com/p/go/downloads/list

Go1.1に僕も上げましたよ。
バージョン上げる時は前のバージョンを一回削除してからインストールするんでしたよね。
Ubuntu12.04(x86)環境では以下の要領でGo1.1に置き換えます。

sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.1.linux-386.tar.gz

簡単ですね。
他OSでのインストール方法については下記を参考にしてください。
http://golang.org/doc/install


Go1.1に置き換えたところで、本題に入りたいと思います。

先月のハッカソンのときにサウンドデータ(Wave)を作りたかったので、バイナリを触ってみました。
今回はバイナリの読み書きについて紹介します。




■バイナリデータをファイルに書き込む
バイナリデータを作成するには、binaryパッケージのWrite関数を使用します。
バイトオーダ(エンディアン)の指定は、binaryパッケージに定義されているLittleEndian、BigEndianを使用します。

バイナリデータを作って、ファイルに書き込むには下記のようにします。
今回はuint8型(1バイト)の「200」を、「test」というファイルに書き込みました。

package main

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

func main() {
 // uint8型(1バイト)の値を用意する
 val := uint8(200)

 // バイナリデータに変換してファイルに出力する
 // バイトオーダはBigEndianとする
 WriteBinaryFile("test", binary.BigEndian, val)
}

/*
バイナリデータに変換してファイルに出力する
*/
func WriteBinaryFile(filename string, order binary.ByteOrder, val interface{}){
 // バイナリデータの格納用
 buf := new(bytes.Buffer)
 // valの値をバイナリデータに変換してbufに格納する
 err := binary.Write(buf, order, val)

 if err != nil {
  fmt.Println("err:", err)
  return
 }

 // ファイル作成 
 file, err2 := os.Create(filename)
 if err2 != nil {
  fmt.Println("file create err:", err2)
  return
 }
 
 // バイナリデータをファイルに書き込み
 _, err3 := file.Write(buf.Bytes())
 if err3 != nil {
  fmt.Println("file write err:", err3)
  return
 }
 
 fmt.Println("file write ok.")
}

「file write ok.」と出力されれば、testファイルが実行ディレクトリに出力されていると思います。

試しにtestファイルをテキストエディタで開いてみましょう。
「200」ではなく、何か1文字入っていると思います。

バイナリエディタを使って開いてみると「C8」と書かれていると思います。
16進でC8は10進の200なので、ちゃんと書き込みが出来ているようですね。


■バイナリファイルを読み込む
次はバイナリファイルのtestファイルを読み込んでみます。
バイナリデータを読み込むには、binaryパッケージのRead関数を使用します。

package main

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

func main() {
 // バイナリファイルを読み込む
 // バイトオーダはBigEndian
 ReadBinaryFile("test", binary.BigEndian)
}

/*
バイナリファイルを読み込む
*/
func ReadBinaryFile(filename string, order binary.ByteOrder) {
 // ファイルを開く
 file, err := os.Open(filename)

 if err != nil {
  fmt.Println("err:", err)
  return
 }

 // ファイルから1バイト読み出し
 b := make([]byte, 1)
 file.Read(b)

 // データ格納用
 var val uint8 
 // バイナリデータからuint8型の値に変換して格納する
 err2 := binary.Read(bytes.NewBuffer(b), order, &val)

 if err2 != nil {
  fmt.Println("err2:", err2)
  return
 }

 fmt.Println("read data:", val)
}

結果:
read data: 200


今回はバイナリデータの変換とファイル読み書きをそれぞれ一緒の関数にしていますが、
別々にした方が扱いやすいと思います。
あと、Waveファイルなどヘッダ情報を書き込むものは、構造体を用意しておくと便利ですよ。

0 件のコメント:

コメントを投稿