Go言語でバイナリファイルを読み込む

f:id:shogonir:20190815235731p:plain

目次

  1. 概要
  2. 準備
  3. ソースコード
  4. 解説
  5. ハマったところ
  6. まとめ

 

1. 概要

ちょっとしたバイナリファイルを読み込たかったので、
Go言語で実装してみたソースコードを備忘録としてのこしておきます。

ソースコードは下記にあります。

github.com

 

2. 準備

プロジェクトのフォルダ構成は下記のようにしました。

01-read-binary
+-- files
|   +-- OneTwoThree
+--- src
    +--- main
        +--- main.go

OneTwoThreeは今回読み込むバイナリファイルです。
main.goが実行するGoのソースコードです。

次に読み込むためのバイナリファイルを準備します。
Linuxだとコマンドでバイナリファイルを作成できるようです。

今回は3バイトのバイナリファイルで、順番に1, 2, 3と格納しようと思います。
というわけで下記のコマンドを実行します。

echo -en "\x01\x02\x03" > files/UInt8ArrayOneToThree

 

3. ソースコード

ソースコード下記の通りです。

package main

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

type ThreeIntegers struct {
    First  uint8
    Second uint8
    Third  uint8
}

func main() {
    readBinaryArray("files/OneTwoThree")
}

func readBinaryArray(path string) {
    fmt.Println("readBinaryArray()")

    file, err := os.Open(path)
    if err != nil {
        fmt.Println("error occured 'os.Open()'")
        panic(err)
    }

    var threeIntegers ThreeIntegers
    errb := binary.Read(file, binary.LittleEndian, &threeIntegers)
    if errb != nil {
        fmt.Println("error occured 'binary.Read()'")
        panic(errb)
    }

    fmt.Println("first  : ", threeIntegers.First)
    fmt.Println("second : ", threeIntegers.Second)
    fmt.Println("third  : ", threeIntegers.Third)
}

 

4. 解説

package mainディレクトリの名前と一致していないといけないです。

importvscodeが勝手にやってくれるので、まぁいいですね。

ThreeIntegers という構造体は uint8 型の1バイトの要素が3つでできます。

os.Open() でファイルを開きます。
相対パスで指定する場合は go run を実行した場所からの相対パスになります。

binary.Read() でバイナリファイルを読み込んで、内容を構造体に詰めます。

最後に構造体の内容を標準出力に出力しています。

 

5. ハマったところ

上記のソースコードに至るまえにはまったのが次のエラー。

panic: reflect: reflect.Value.SetUint using value obtained using unexported field

goroutine 1 [running]:
reflect.flag.mustBeAssignable(0x1aa)
        /Users/shogo/.goenv/versions/1.11.4/src/reflect/value.go:231 +0x1ee
reflect.Value.SetUint(0x10a5940, 0xc000090000, 0x1aa, 0x61616161)
        /Users/shogo/.goenv/versions/1.11.4/src/reflect/value.go:1551 +0x2f
encoding/binary.(*decoder).value(0xc000076e78, 0x10a5940, 0xc000090000, 0x1aa)
        /Users/shogo/.goenv/versions/1.11.4/src/encoding/binary/binary.go:552 +0x8ac
encoding/binary.(*decoder).value(0xc000076e78, 0x10aff60, 0xc000090000, 0x199)
        /Users/shogo/.goenv/versions/1.11.4/src/encoding/binary/binary.go:523 +0x2c5
encoding/binary.Read(0x10dc060, 0xc000086018, 0x10dc440, 0x1187f88, 0x10a08c0, 0xc000090000, 0xc00008c000, 0xc000076f18)
        /Users/shogo/.goenv/versions/1.11.4/src/encoding/binary/binary.go:248 +0x342
main.readBinaryArray(0x10ca1bc, 0x1a)
        /Users/shogo/prg/github/go-sample/01-read-binary/src/main/main.go:25 +0x124
main.main()
        /Users/shogo/prg/github/go-sample/01-read-binary/src/main/main.go:14 +0x36
exit status 2

エラーメッセージを読んでもさっぱり分からなかったですが、
調べてみたら構造体のフィールド名が public なのに小文字から始まっていたからでした。

type ThreeIntegers struct {
    first  uint8
    second uint8
    third  uint8
}

正しくは下記。

type ThreeIntegers struct {
    First  uint8
    Second uint8
    Third  uint8
}

 

6. まとめ

ちょっとしたバイナリを読み込むにはC/C++より手軽に実装できてよかったです。

気に入ったのは、vscodeがとにかくいろいろ補完が気がきくとこでした。
コードを書いている時のストレスはかなり少なかったと思います。

これからも使えそうな場所ではどんどんGoを使っていきたいと思います。