概要
Tour of Go の - Readers についてのサンプル。
Goのio.Reader
インターフェースは、何かを読み取る基本的なインターフェースです。
このインターフェースを実装している型はとてもいっぱいあります。
インターフェースの定義は以下のようになっています。
(io.go ファイルより)
type Reader interface { Read(p []byte) (n int, err error) }
とてもシンプル。綺麗ですよね。ReaderがあるってことはWriterも当然あります。
type Writer interface { Write(p []byte) (n int, err error) }
この他にも、io.go
ファイルを見るとインターフェースがいっぱいあります。見てるととても面白いです。
Golandとか使っている場合、インターフェースや型の横にアノテーションで、実装しているインターフェースとか実装されている型とかを確認できるので、いい勉強にもなります。
どれも、小さなインターフェースで構築してあって、組み合わせやすいようになっています。
で、Tour of Goでサンプルとして記載されているものとして、strings.Reader
があります。
この型もio.Reader
インターフェースを実装しているので、当然 Read
メソッドを持っています。
文字列をストリームのようにして扱いたいときによく利用します。
ちなみに、strings.Reader
は、ioパッケージの以下のインターフェースを実装しています。
- ByteReader
- ByteScanner
- ReadSeeker
- Reader
- ReaderAt
- RuneReader
- RuneScanner
- Seeker
- WriteTo
文字列のストリームですので、閉じる必要がない。なので、strings.Reader
はio.Closer
は実装していないってことですね。逆に os.File
とかになると内部リソースを開放する必要があるので、io.Closer
を実装しているって感じです。
サンプル
package tutorial import ( "errors" "fmt" "io" "strings" ) // Reader は、 Tour of Go - Readers (https://tour.golang.org/methods/21) の サンプルです。 func Reader() error { // ------------------------------------------------------------ // Go言語の io.Reader インターフェース // io.Readerインターフェースは、何かを読み取る基本的なインターフェース // このインターフェースを実装している型はとても多い。 // // io.Reader インターフェースには、以下の定義がある // - Read(b []byte) (n int, err error) // // 基本的なものとして、 strings.Reader がある // これは、他の言語の StringReader と同じような使い方が出来る // // n に読み込んだバイト数が設定されるので、それでバッファから取り出して // 末尾まで読み込むと、 err に io.EOF が設定されるので // それを判断して処理を中断するというのが基本パターン. // ------------------------------------------------------------ var ( message = "hello world" reader = strings.NewReader(message) chunkSize = 4 buf = make([]byte, chunkSize) results = make([]byte, 0, len(message)) ) // 4 バイトずつ読み込み for { n, err := reader.Read(buf) fmt.Printf("[reader] read: %dbytes\terror: %v\tvalue: %s\n", n, err, buf[:n]) if n != 0 { results = append(results, buf[:n]...) } if errors.Is(err, io.EOF) { break } } fmt.Printf("[result] %s\n", string(results)) // (補足) 上の例ではRead用のバッファと結果用のバッファの2つを使っているが // 一つのバッファで読み込み続けることも可能。2つバッファ使った方が // わかりやすいので個人的にはあまり使わない。 var ( from = 0 to = 0 readBytes = 0 ) // 1 バイトずつ読み込み reader = strings.NewReader(message) buf = make([]byte, len(message)) for { from, to = readBytes, readBytes+1 if len(message) < to { break } n, err := reader.Read(buf[from:to]) readBytes += n if n == 0 || err != nil { break } } fmt.Printf("[result] %d bytes. (%s)\n", readBytes, string(buf)) return nil }
try-golang/tutorial_gotour_25_reader.go at master · devlights/try-golang · GitHub
実行すると以下な感じ。
[Name] "tutorial_gotour_reader" [reader] read: 4bytes error: <nil> value: hell [reader] read: 4bytes error: <nil> value: o wo [reader] read: 3bytes error: <nil> value: rld [reader] read: 0bytes error: EOF value: [result] hello world [result] 11 bytes. (hello world)
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場