Go 言語のいろいろなパッケージを使ってみるシリーズ(目次)。 以前の2つの記事
で io パッケージに定義されている型や関数を使ってみましたが、今回は io パッケージに定義されている残りのパッケージ関数を使っていきます。 まぁ、関数のシグニチャ(名前や引数)を見れば大体動作は分かりそうな関数ばっかりだし、パッケージドキュメントに簡潔な説明とサンプルコードが載ってますが。
【この記事の内容】
io パッケージに定義されている関数
今回見ていく io パッケージの関数:// コピー関連 func Copy(dst Writer, src Reader) (written int64, err error) func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) func CopyN(dst Writer, src Reader, n int64) (written int64, err error) // 読み込み関連 func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) func ReadFull(r Reader, buf []byte) (n int, err error) // 書き出し関連 func WriteString(w Writer, s string) (n int, err error)
コピー関連
io.Copy 関数
io.Copy 関数は、第2引数の io.Reader から io.EOF に達するまで内容を読み込み、第1引数の io.Writer に書き出します:src := strings.NewReader("Hello, world!") var dst bytes.Buffer // io.Copy 関数 io.Copy(os.Stdout, src) // Output: // Hello, world!
引数の順序は、通常の変数への値の代入「x := "Hello, world"」と同じ順序になっています。 返り値はコピーしたバイト数とコピー時のエラー(なければ nil)です:
src := strings.NewReader("Hello, world!") var dst bytes.Buffer // io.Copy 関数 n, err := io.Copy(&dst, src) if err != nil { log.Fatal(err) } fmt.Printf("%s (%d bytes)", dst.String(), n) // Output: // Hello, world! (13 bytes)
読み込みは io.EOF に達するまでされますが、コピーが成功した場合には第2返り値のエラーは io.EOF ではなく nil です。
引数の io.Reader が io.WriterTo インターフェースを実装していればその WriteTo メソッドを使い、引数の io.Writer が io.ReaderFrom インターフェースを実装していればその ReadFrom メソッドが使われます(WriterTo 優先)。
io.CopyBuffer 関数
io.CopyBuffer 関数は、基本的には io.Copy 関数と同じですが、コピー時に(必要なら)第3引数のバイトスライスをバッファとして使います:src := strings.NewReader("Hello, world!") var dst bytes.Buffer buf := make([]byte, 1024) // io.CopyBuffer 関数 n, err := io.CopyBuffer(&dst, src, buf) if err != nil { log.Fatal(err) } fmt.Printf("%s (%d bytes)", dst.String(), n) // Output: // Hello, world! (13 bytes)
おそらく引数の io.Reader や io.Writer が io.WriterTo や io.ReaderFrom を実装していれば、バッファは使われないかと思います。
指定したバイトスライスが nil なら別にバッファが割り当てられ、長さが0ならパニックを起こします:
io.CopyBuffer(&dst, src, nil) // 新たにバッファが割り当てられる io.CopyBuffer(&dst, src, make([]byte, 0)) // パニックを起こす
io.CopyN 関数
io.CopyN 関数は指定した第2引数の io.Reader から第1引数の io.Writer へ、第3引数で指定したバイト数だけコピーします:src := strings.NewReader("Hello, world!") var dst bytes.Buffer // io.CopyN 関数 n, err := io.CopyN(&dst, src, 5) if err == io.EOF { fmt.Printf("%s (%d bytes)", dst.String(), n) } else if err != nil{ log.Fatalf("%d bytes are copied: %v", n, err) } else { fmt.Println(dst.String()) // 5バイトを読み取って書き出す } // Output: // Hello
io.Reader から読み込めるバイト数が指定したより少ない場合、io.EOF が返されます:
src := strings.NewReader("Hello, world!") // 13バイト var dst bytes.Buffer // io.CopyN 関数 n, err := io.CopyN(&dst, src, 20) // 20 バイトをコピー if err == io.EOF { // 読み込めるバイト数が少ない場合 fmt.Printf("%s (%d bytes)", dst.String(), n) } else if err != nil{ log.Fatal(err) } else { fmt.Println(dst.String()) // 5バイトを読み取って書き出す } // Output: // Hello, world! (13 bytes)
io.EOF が返された場合でも、ある分のバイトはコピーされているようです。
引数の io.Writer が io.ReaderFrom インターフェースを実装していれば、その ReadFrom メソッドを使います。
読み込み関連
io.ReadAtLeast 関数
io.ReadAtLeast 関数は、第1引数の io.Reader から第2引数のバイトスライスへ、第3引数で指定したバイト数以上のバイトを読み込みます。 指定したバイト数を読み込めなかった場合は io.ErrUnexpectedEOF エラーを返します(ただし、読み込んだバイト数が0の場合は io.EOF を返します):r := strings.NewReader("Hello, world!") // 13バイト buf := make([]byte, 1024) // io.ReadAtLeast 関数 n, err := io.ReadAtLeast(r, buf, 10) // 10バイト以上を読み込む if err == io.ErrUnexpectedEOF { fmt.Printf("%s (%d bytes)", string(buf), n) } else if err != nil{ log.Fatal(err) } else { // 今の場合はここが実行される fmt.Println(string(buf)) } // Output: // Hello, world!
このコードで io.ReadAtLeast 関数の第3引数を15にすると io.ErrUnexpectedEOF が返されます:
r := strings.NewReader("Hello, world!") // 13バイト ... // io.ReadAtLeast 関数 n, err := io.ReadAtLeast(r, buf, 15) // 15バイト以上を読み込む if err == io.ErrUnexpectedEOF { // 今の場合はここが実行される fmt.Printf("%s (%d bytes)", string(buf), n) } else if err != nil{ ... } // Output: // Hello, world! (13 bytes)
指定したバイト数が第2引数のバイトスライスの長さより大きければ io.ErrShortBuffer エラーが返されます:
n, err := io.ReadAtLeast(r, make([]byte, 20), 25) // err に io.ErroShortBuffer がセットされる
io.ReadFull 関数
io.ReadFull 関数は、上記の io.ReadAtLeast 関数で第3引数のバイト数に第2引数のバイトスライスの長さを指定したものと同じです(実装は知りませんが):r := strings.NewReader("Hello, world!") // 13バイト buf := make([]byte, 10) // io.ReadFull 関数 n, err := io.ReadFull(r, buf) if err == io.ErrUnexpectedEOF { fmt.Printf("%s (%d bytes)", string(buf), n) } else if err != nil{ log.Fatal(err) } else { // 今の場合はここが実行される fmt.Println(string(buf)) } // Output: // Hello, wor
書き出し関連
io.WriteString 関数
io.WriteString 関数は、第1引数の io.Writer に文字列を書き出します。 引数の io.Writer が(適当なシグニチャの) WriteString メソッドを持っていればそれを使います。 そうでなければ Write メソッドを使いますが、一度しか Write メソッドを呼び出しません。 返り値は書き込んだバイト数とエラー(なければ nil)です:var buf bytes.Buffer // io.WriteString 関数 io.WriteString(&buf, "Hello, world!") fmt.Println(buf.String()) // Output: // Hello, world!
この io.WriteString 関数は、WriteString メソッドを持つ io.Writer に対しては、文字列をバイトスライスへ変換して io.Writer の Write メソッドで書き出すよりも効率的です:
// io.WriteString は以下の Write メソッド呼び出しより効率的 &buf.Write([]byte("Hello, world!"))
今回は io パッケージに定義されているパッケージ関数を使ってみました。 基本的な使い方は名前などから分かりますが、返されるエラーは少し注意が必要かも知れませんね。 あと、io.Copy 関数はまぁよく使いそうですが、io.WriteString 関数はついつい文字列をバイトスライスに変換して Write メソッドで済ませてしまいそうなので、意識して使っていった方がよさそうです。
次回は入出力をきちんと扱う場合に io パッケージよりも使いそうな bufio パッケージを使ってみる予定。
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者: Alan A.A. Donovan,Brian W. Kernighan,柴田芳樹
- 出版社/メーカー: 丸善出版
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る