Go の io.Reader 覚書

備忘録。

Go の io.Reader の使い方について。

なぜ io.Reader

これはファイルだろうと文字列だろうと全て同じように扱えるようにするため。

標準/3rd party 問わず、文字列もしくはバイト列を受け取るようなライブラリ関数の引数は string でも []byte でもなく io.Reader を受け取るようになっていることが多い。(([]byte も受け取れるようになっているものはあるが、string を受け取るものはまず見かけない))

自分でバイト列を受け取るライブラリを作る場合も io.Reader を受け取るようにし、テストコードなどでは []bytestring から io.Reader に変換して入力データを作ればいい。入力データが []byte の場合は bytes.NewReader を、string の場合は strings.NewReader を使う。

ユースケース

(随時追記予定)

N バイトずつ読み込む

io.ReadFull を使う。残りが0バイトの時は io.EOF を返すが、N バイト未満だった場合は io.ErrUnexpectedEOF を返すのでちょっと注意が必要。

以下は文字列を最大8バイトずつの [][]byte に分割する例。

func main() {
    r := strings.NewReader("01234567890123456789")
    result := make([][]byte, 0)
    for i := 0; ; i++ {
        buf := make([]byte, 8)
        n, err := io.ReadFull(r, buf)
        if err == io.EOF {
            break
        } else if err == nil {
            result = append(result, buf)
        } else if err == io.ErrUnexpectedEOF {
            dst := make([]byte, n)
            copy(dst, buf)
            result = append(result, dst)
        } else {
            log.Fatal(err)
        }
    }

    fmt.Printf("%s\n", result)
}