目次
目次
io パッケージについて
io package は、ディスク上のファイルからキーボード入力まであらゆる入力と出力のインタフェースを提供しています。
$ gocloc --not-match=test io
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Go 12 212 673 1092
-------------------------------------------------------------------------------
TOTAL 12 212 673 1092
-------------------------------------------------------------------------------
Reader
ReadAll
最初に 512 バイトのバッファを確保し、EOF に到達するまでキャパシティを大きく取りながら読みみます。 サイズの取り方は、Reader interface の Read で実装しています。
ファイルの場合は事前に分かるため buffer の大きさだけ読みます。
文字列やバイトの場合は、長さがわからないため、徐々にキャパシティを大きくしながら読みます。
Read メソッドにキャパシティの取り方を任せることで、ケースごとに効率の良い読み取りの実装がされています。
ReadAtLeast
指定した位置まで Buffer にデータを読みます。
途中まで読むケースにおいて、予め位置がわかっていれば、Read のキャパシティを徐々に大きくする処理を抑えることができます。
実用的な利用ケースは思いつかないのですが、外部ファイルなど非常に大きなサイズになる可能性があるケースにおいて安全に扱うことができそうです。
ReadFull
Buffer の長さがキャパシティと同一、つまり Buffer にデータが満たされていることをが保証される。len!=cap
の場合は、EOF がエラーで返されます。
データが正しく読まれることを保証しコードを単純にできます。
b := []byte("0123")
r := bytes.NewReader(b)
buf := make([]byte, 10)
n, err := io.ReadFull(r, buf)
if err != nil {
log.Fatal(err) // EOF error
}
b := []byte("0123")
r := bytes.NewReader(b)
buf := make([]byte, 4)
n, err := io.ReadFull(r, buf)
if err != nil { // OK
log.Fatal(err)
}
Writer
WriteString
Writer の Write メソッドを呼び出しです。実装されていない場合は、文字列をバイトに入れ Write メソッドを実行します。
Copy
Copy は Buffer を呼び出しごとに割り当てますが、CopyBuffer を利用し自分で割り当てるることもできます。
// https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/io/io.go;l=415;bpv=1;bpt=1
if buf == nil {
size := 32 * 1024
if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
if l.N < 1 {
size = 1
} else {
size = int(l.N)
}
}
buf = make([]byte, size)
}
CopyBuffer
Reader が WriteTo メソッドが実装されている場合はそれを利用します。
strings > Strings WriteTo では、io の WriteString を呼び出し、Writer の WriteString メソッドを呼びます。
Writer に ReadFrom メソッドがある場合はそれを利用します。
どちらも実装されていない場合は、Reader の Read と Writer の Write を利用します。
CopyN
Copy は外部ファイルなど最大バイト数がわからず、想定以上に読み込まれる可能性があります。
CopyN では書き込むバイト数を指定できます。そのため、大きなキャパシティになる可能性がある場合でも安全に扱うことができます。
PipeReader/PipeWriter
Pipe
The data is copied directly from the Write to the corresponding Read (or Reads); there is no internal buffering. (go doc)
バッファリングなしで直接扱えるのでメモリ効率が良くなるようです。
Examples For Using io.Pipe in Go - zupzup
こちらのブログを読んで理解したのですが、例えば HTTP リクエストの結果を PipeWriter で取得し、直接 PipeReader に送ることで、余計なメモリ確保が不要になるという解釈をしました。
TeeReader
Reader をラップした新しいリーダーを返します。
TeeReader の Read は、渡した Reader の中身を読み Writer で書き込むため、もとの Reader に影響がありません。
『net/http でレスポンスの内容を確認したいなら io.TeeReader を使おう』 のような使い方ができるようです。