Go 言語のいろいろなパッケージを使ってみるシリーズ(目次)。 今回は io パッケージに定義されている具象 io.Reader, io.Writer を見ていきます。 io パッケージに定義されているインターフェース型については「Go 言語の io パッケージに定義されているインターフェース型を一気見する」を参照。
【この記事の内容】
io パッケージに定義されている具象 io.Reader, io.Writer
io パッケージには io.Reader/io.Writer をラップして各種機能を提供する具象 io.Reader, io.Writer がいくつか定義されています。 それらはまた io.Reader/io.Writer のいずれかを実装しているので、使い方は簡単です:// io.Reader 型、io.Reader を返すメソッド func MultiReader(readers ...Reader) Reader func TeeReader(r Reader, w Writer) Reader type LimitedReader func LimitReader(r Reader, n int64) Reader type SectionReader func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader // io.Writer を返すメソッド func MultiWriter(writers ...Writer) Writer // パイプ func Pipe() (*PipeReader, *PipeWriter) type PipeReader type PipeWriter
io.Reader を返すメソッド
io.MultiReader 関数
io.MultiReader 関数が返す io.Reader オブジェクトは、指定した複数の io.Reader から順に内容を読み込みます:r0 := strings.NewReader("Hello, world0!\n") r1 := strings.NewReader("Hello, world1!\n") r2 := strings.NewReader("Hello, world2!\n") // io.MultiReader mr := io.MultiReader(r0, r1, r2) content, err = ioutil.ReadAll(mr) if err != nil { log.Fatal(err) } fmt.Println(string(content)) // Output: // Hello, world0! // Hello, world1! // Hello, world2!
まぁ特に難しくはありませんね。
io.TeeReader 関数
io.TeeReader 関数が返す io.Reader オブジェクトは、読み込んだ内容を指定した io.Writer に書き込みます:logFile, _ := os.Create("log.txt") defer logFile.Close() // io.TeeReader tr := io.TeeReader(strings.NewReader("Hello, world!\n"), logFile) content, err = ioutil.ReadAll(tr) if err != nil { log.Fatal(err) } fmt.Println(string(content)) // Output: // Hello, world!
io.Reader から読み取った「Hello, world!」という文字列は標準出力に表示されますが、読み込んだ時点で log.txt ファイルに読み取った内容が書き込まれます:
// log.txt ファイル Hello, world!
io.LimitReader 関数
io.LimitReader 関数は、指定したバイト数だけを読み取れる io.Reader を返します:// io.LimitReader lr := io.LimitReader(strings.NewReader("Hello, world!"), 5) content, err := ioutil.ReadAll(lr) if err != nil { log.Fatal(err) } fmt.Println(string(content)) // Output: // Hello
引数として渡した io.Reader の内容「Hello, world!」のうち、5バイトの「Hello」のみが読み取れます。
io パッケージには、以下のように定義される LimitedReader 型も公開されているので
type LimitedReader interface{ R Reader N int64 }
同じことを構造体の作成を使ってもできます:
// LimitedReader lr := &io.LimitedReader{strings.NewReader("Hello, world!"), 5} content, err := ioutil.ReadAll(lr) if err != nil { log.Fatal(err) } fmt.Println(string(content)) // Output: // Hello
まぁ、LimitedReader 型に Read メソッド以外の便利なメソッドが定義されているわけでもないので、普通に LimitReader 関数を使っておく方が無難かと思いますが。
io.SectionReader 関数
io.SectionReader は指定した位置から指定したバイト数だけ読み取ります。 インスタンスを取得するには NewSectionReader 関数を使います。 io.SectionReader には以下のようなメソッド- func (s *SectionReader) Read(p []byte) (n int, err error)
- func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error)
- func (s *SectionReader) Seek(offset int64, whence int) (int64, error)
- func (s *SectionReader) Size() int64
が定義されていて、io.Reader, io.ReaderAt, io.Seek インターフェースを実装しています。 まぁ、ランダムアクセスできるリーダーって感じですかね。 使い方は以下のようになります:
// SectionReader sr := io.NewSectionReader(strings.NewReader("Hello, world!"), 3, 5) content, err = ioutil.ReadAll(sr) if err != nil { log.Fatal(err) } fmt.Println(string(content)) // Output: // lo, w
引数の io.Reader の内容「Hello, world!」に対して、位置3から5バイト分を読み取って「lo, w」と表示します。 引数の io.Reader に指定した内容分のバイトが含まれていなければ、ある分だけの内容を読み取ります。
io.Writer を返すメソッド
io.MultiWriter 関数
io.MultiWriter 関数が返す io.Writer オブジェクトは、複数の io.Writer に同じ内容を書き出します:logFile, _ := os.Create("log.txt") defer logFile.Close() // MultiWriter mw := io.MultiWriter(os.Stdout, logFile) io.WriteString(mw, "Hello, world!") // io.WriteString 関数は io.Writer に文字列を書き出す // Output: // Hello, world!
これを実行すると、標準出力に「Hello, world!」と表示され、log.txt ファイルにも同じ内容が書き込まれます。
io.Pipe 関数
io.Pipe 関数はパイプされた io.Reader/io.Writer の組を返します。 パイプされたファイルの組を返す、同様の関数 os.Pipe 関数は「Go 言語の os パッケージにある File 型を使ってみる (1) : os.File オブジェクトを取得する関数」で使ってみました。 そこで見たサンプルコードとほぼ同じコードを io.Pipe 関数を使って書いてみると、以下のようになります:// io.Pipe 関数 r, w := io.Pipe() go func(){ for i := 0; i < 10; i++ { io.WriteString(w, fmt.Sprintf("Hello, world%d!\n", i)) // io.WriteString で io.Writer に文字列を書き込む time.Sleep(100*time.Millisecond) } if err := w.Close(); err != nil { log.Fatal(err) } }() bs := make([]byte, 20) for { time.Sleep(1*time.Second) n, err := r.Read(bs) if err == io.EOF { break } else if err != nil { log.Panic(err) } fmt.Print(string(bs[:n])) } if err := r.Close(); err != nil { log.Fatal(err) }
コードは大体同じですが io.Writer は(*os.File と違って)WriteString メソッドを持っていないので、io.WriteString 関数を使って文字列を書き込んでいる箇所が少し違います*1。 io.Pipe 関数と os.Pipe 関数の使い分けがイマイチよく分かりませんが、ファイルを使いたい場合以外は io.Pipe 関数を使っておけばいいんでしょうかね?*2
今回は io パッケージに定義されている具象 io.Reader, io.Writer の使い方を見てきました。 まぁ、汎用的な用途のあるものばかりなので、使い方に迷うことはなさそうです。 次回は io パッケージに定義されているパッケージ関数を使ってみます。
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者: Alan A.A. Donovan,Brian W. Kernighan,柴田芳樹
- 出版社/メーカー: 丸善出版
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る