io.Reader と io.Writer
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 73/107。
Goの標準ライブラリは、2つの基本的なインターフェースである io.Reader と io.Writer を通じて、インターフェースベースの設計を体現しています。これらのシンプルなインターフェースは、ファイル操作からネットワーク通信に至るまで、あらゆる機能を支えています。
io.Reader インターフェースは、たった1つのメソッドを必要とします:
type Reader interface {
Read(p []byte) (n int, err error)
}それは最大で len(p) バイトをスライスに読み込み、読み込まれたバイト数を返します。同様に、io.Writer は次のように定義しています:
type Writer interface {
Write(p []byte) (n int, err error)
}これらのメソッドを実装する任意の型は、I/Oエコシステム全体で動作します。
以下は、io.Readerを実装するカスタム型です:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}これで、このカスタムリーダーは io.Reader を期待する任意の関数で使用できるようになります:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAAこの設計により、io.Readerやio.Writerを受け入れる関数を記述できるようになり、ファイル、ネットワーク接続、バッファ、または任意のカスタム実装で動作させることができます。
チャレンジ
簡単io.Reader と io.Writer インターフェースを実装する、カスタムテキストストリームプロセッサを構築しましょう!データが流れる際にテキストを変換できる型を作成し、これらの基本的なインターフェースがどのように強力な I/O 構成を可能にするかを実証します。
コードは 2 つのファイルに分けて構成します:
streams.go: カスタムのリーダー型とライター型を定義します。文字列をラップし、読み取られたバイト数を追跡する
CountingReader構造体を作成します。ソース文字列用と位置トラッカー用のフィールドを持たせる必要があります。提供されたスライスにソースからバイトを読み取り、位置を更新し、ソースが使い果たされたときにio.EOFを返すRead(p []byte) (int, error)メソッドを実装してください。書き込まれたデータを収集し、大文字に変換する
UppercaseWriter構造体を作成します。累積された結果を内部に保存する必要があります。入力されたバイトを大文字に変換して保存するWrite(p []byte) (int, error)メソッドを実装してください。累積された大文字のテキストを取得するためのResult() stringメソッドを追加します。型を適切に初期化するために、コンストラクタ関数
NewCountingReader(source string) *CountingReaderとNewUppercaseWriter() *UppercaseWriterを作成します。main.go: 標準ライブラリと連携して動作するカスタム I/O 型を実演します。文字列入力を読み取り、
CountingReaderをio.ReadAllと共に使用してすべてのデータを読み取ります。そのデータをUppercaseWriterのWriteメソッドを使用して渡します。結果を次の形式で出力してください:
Original: [input] Uppercase: [result from writer] Bytes read: [total bytes]
以下の入力が提供されます:
- テキスト 1 行
例えば、次のように与えられた場合:
Hello World出力は次のようになります:
Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11また、次のように与えられた場合:
Go interfaces are powerful出力は次のようになります:
Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26重要なポイントは、CountingReader が io.Reader を実装しているため、io.ReadAll とシームレスに連携することです。Go のエコシステムにおいて io.Reader を受け入れる関数であれば、どのようなものでも作成したカスタム型で動作します。これがインターフェースベースの設計の力です。
チートシート
Goのio.Readerおよびio.Writerインターフェースは、I/O操作の基本です:
type Reader interface {
Read(p []byte) (n int, err error)
}type Writer interface {
Write(p []byte) (n int, err error)
}Readメソッドは、スライスに最大len(p)バイトを読み込み、読み取ったバイト数を返します。Writeメソッドは、スライスからバイトを書き込み、書き込んだバイト数を返します。
これらのメソッドを実装する型は、GoのI/Oエコシステム全体で動作します。カスタムリーダーの例:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}標準ライブラリ関数でカスタムリーダーを使用する:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAAこの設計により、io.Readerまたはio.Writerを受け取る関数は、ファイル、ネットワーク接続、バッファ、または任意のカスタム実装で動作できるようになります。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 入力を読み込む
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
// 末尾に改行があれば削除する
if len(input) > 0 && input[len(input)-1] == '\n' {
input = input[:len(input)-1]
}
// TODO: 入力文字列を使用して CountingReader を作成する
// countingReader := NewCountingReader(input)
// TODO: io.ReadAll を使用して CountingReader からすべてのデータを読み込む
// data, err := io.ReadAll(countingReader)
// TODO: UppercaseWriter を作成する
// upperWriter := NewUppercaseWriter()
// TODO: UppercaseWriter にデータを書き込む
// upperWriter.Write(data)
// TODO: 以下の必要なフォーマットで結果を出力する:
// Original: [input]
// Uppercase: [result from writer]
// Bytes read: [total bytes]
// 未使用のインポートエラーを避けるためのプレースホルダー - 実装時に削除してください
_ = io.EOF
fmt.Println("Original:", input)
// fmt.Println("Uppercase:", upperWriter.Result())
// fmt.Println("Bytes read:", countingReader.Position)
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー11標準ライブラリとOOP
io.Reader と io.Writersort.Interfacefmt.Stringer インターフェースencoding/json と構造体http.Handler インターフェースまとめ:REST API モデル