【Go】標準ライブラリの io.Reader/io.Writer 機能
Go の入出力処理といえば io.Reader
/ io.Writer
インターフェースです。
このインターフェースを扱うための関数が標準ライブラリにもたくさん用意されていますが、Goを使い始めたばかりだと知らないものが多いと思いますし、使わないうちに忘れてしまうこともありましたので、代表的なものを書き出しておくことにしました。
入出力の対象となるオブジェクト
入出力の具体的な対象を表すオブジェクトです。例えばファイルや通信ソケットなどです。
io.Reader
/ io.Writer
を満たす構造体やそのポインタ型となります。
ファイル
ファイルとの入出力に使用します。
- 型:
*os.File
- 入出力:両方可能(
io.Reader
|io.Writer
)- ただし、ファイルのオープン方法によって、読み書き可能なのか、一方のみ可能なのかが変わってくる。違反した場合は実行時エラーとなる。
- 生成方法:
-
os.Open
関数:既存のファイルを読み込みたい場合。 -
os.Create
関数:ファイルを新規作成して書き込みたい場合。 -
os.OpenFile
関数:既存のファイルに追記したい場合や、読み書きの両方を行いたい場合など、オープン方法を細かく制御する場合。
-
標準入出力
コマンドプロンプトやターミナルとの入出力に使用します。
- 型:
*os.File
- 入出力:両方可能(
io.Reader
|io.Writer
)- 一種のファイルとして扱われるため両方のインターフェースを実装してはいるが、実際にはそれぞれ片方のみ可能。
- 生成方法:
ネットワーク(通信ソケット)
TCP/UDPなどのネットワーク通信に使用します。
- 型:
*net.Conn
- 入出力:両方可能(
io.Reader
|io.Writer
) - 生成方法:
-
net.Dial
関数:通常の生成方法。 -
net.DialTimeout
関数:タイムアウトを指定したい場合。 -
net.FileConn
:*os.File
を*os.Conn
として扱いたい場合。
-
一時的なバイト配列バッファ(読み書き両用)
[]byte
型の変数を io.Reader
や io.Writer
として扱いたい場合に使用します。
先頭から指定バイトずつ読み出したり末尾にデータを追加したりといった、便利なメソッドが用意されています。
- 型:
*bytes.Buffer
- 入出力:両方可能(
io.Reader
|io.Writer
) - 生成方法:
- 通常の変数宣言(
var x bytes.Buffer
):空のバッファを生成する場合。インタフェースを満たすのは*bytes.Buffer
型のため、他の処理で使用するときには&x
のようにポインタ型とする必要がある。 -
bytes.NewBuffer
関数:[]byte
型の変数からバッファを生成する場合。 -
bytes.NewBufferString
関数:string
型の変数からバッファを生成する場合。
- 通常の変数宣言(
一時的なバイト配列バッファ(読み込み専用)
[]byte
型の変数を io.Reader
として扱いたい場合に使用します。*bytes.Buffer
に似ていますが、こちらは読み込み専用である代わりにファイルのようにシーク(読み込み開始位置の移動)をすることができます。
- 型:
*bytes.Reader
- 入出力:読み込み(
io.Reader
) - 生成方法:
-
bytes.NewReader
関数:[]byte
型の変数からバッファを生成する。
-
文字列構築用バッファ(書き込み専用)
string
型の変数を io.Writer
として扱い、文字列を少しづつ構築していくための専用バッファとして利用したい場合に使用します。後で構築した文字列を取り出せます。
文字列を組み立てていくときに多用する +
演算子だと、大きな文字列を作成する場合には何度もメモリ拡張が走ってしまい非効率的ですが、これを使うとあらかじめキャパシティを指定することができますし、他にも便利なメソッドを利用することができます。
- 型:
*strings.Builder
- 入出力:書き込み(
io.Writer
) - 生成方法:
- 通常の変数宣言(
var x strings.Builder
):空のバッファを生成する。インタフェースを満たすのは*strings.Builder
型のため、他の処理で使用するときには&x
のようにポインタ型とする必要がある。
- 通常の変数宣言(
文字列読み込み用バッファ(読み込み専用)
string
型の変数を io.Reader
として扱いたい場合に使用します。
ファイルのようにシーク(読み込み開始位置の移動)をすることができます。
- 型:
*strings.Reader
- 入出力:読み込み(
io.Reader
) - 生成方法:
-
strings.NewReader
関数:string
型の変数からバッファを生成する。
-
パイプ連結
io.Writer
に書き込まれた内容を io.Reader
から取り出せるようにするためのパイプ用のオブジェクトを生成します。どんどん書き込まれてくる文字列を別の出力に流し込むような用途で使用します。
- 型:
*io.PipeReader
、*io.PipeWriter
- 入出力:それぞれ、読み込み(
io.Reader
)と書き込み(io.Writer
) - 生成方法:
-
io.Pipe
関数:*io.PipeReader
と*io.PipeWriter
のペアを生成する。
-
入出力を実行するための関数・構造体
io.Reader
/ io.Writer
を引数に取り、io.Reader
からデータを読み込んだり、io.Writer
へデータを書き出したりするために用意されている関数です。また、そのようなメソッドを複数持った構造体として機能が提供される場合もあります。
読み込み
関数 | 説明 |
---|---|
io.ReadAll |
末尾(EOF)まで読み込む。 |
io.ReadFull |
指定したバイト数だけ確実に読み込む。指定したバイト数に満たない場合はエラーとなる。 |
io.ReadAtLeast |
最低限読み込むバイト数を指定するが、それ以上のデータもあれば読み込む。指定したバイト数に満たない場合はエラーとなる。 |
fmt.Fscan |
テキストをスキャンして、スペースや改行文字で区切れられた任意の数の値を引数に格納する。 |
fmt.Fscanf |
fmt.Fscan の機能に加えて、書式(例:"%s %d\n")を指定できる。 |
fmt.Fscanln |
fmt.Fscan と同じ機能だが、末尾の改行文字かEOFまでをスキャンする。 |
binary.Read |
バイナリデータ(バイト配列)を指定したバイトオーダー(エンディアン)で任意の変数に読み込む。 |
構造体 | 説明 | ファクトリ関数 |
---|---|---|
bufio.Scanner |
トークンごとにデータを読み込むことができる。デフォルトでは改行文字でトークンが区切られるが変更可能。 | bufio.NewScanner |
json.Decoder |
JSON形式の文字列を解析して変数に格納する(デコードする)。 | json.NewDecoder |
csv.Reader |
CSV形式の文字列を解析して文字列型のスライスとして読み込む。 | csv.NewReader |
tar.Reader |
tar形式のアーカイブから格納されたファイルの内容を読み込む。 | tar.NewReader |
書き込み
関数 | 説明 |
---|---|
io.WriteString |
文字列を書き込む。対象が io.StringWriter インターフェースを満たしている場合は、対象が持つ WriteString 関数を優先的に使用する。 |
fmt.Fprint |
任意の数の引数を文字列化してスペース区切りで書き込む。 |
fmt.Fprintf |
fmt.Fprint の機能に加えて、書式(例:"%s %d\n")を指定できる。 |
fmt.Fprintln |
fmt.Fprint の機能に加えて、末尾に改行文字を書き込む。 |
binary.Write |
任意の変数をを指定したバイトオーダー(エンディアン)でバイナリデータ(バイト配列)に変換して書き込む。 |
構造体 | 説明 | ファクトリ関数 |
---|---|---|
json.Encoder |
変数の値をJSON形式の文字列に変換して書き込む(エンコードする)。 | json.NewEncoder |
csv.Writer |
文字列型のスライスをCSV形式の文字列に変換して書き込む。 | csv.NewWriter |
tar.Writer |
データをtar形式のアーカイブ内のファイルとして書き込む。 | tar.NewWriter |
その他
関数 | 説明 |
---|---|
io.Copy |
引数に渡した io.Reader から io.Writer へコピーする。 |
io.CopyN |
io.Copy と同じだが、指定したバイト数だけコピーする。 |
io.CopyBuffer |
io.Copy と同じだが、引数で渡した []byte 変数を一時バッファとして利用する。 |
新しい io.Reader/io.Writer を生成する関数
既存の io.Reader
/ io.Writer
を満たすオブジェクトをラップして、変換処理などの追加機能を行う新しいオブジェクトを生成する関数が用意されています。生成されたオブジェクトもまた io.Reader
/ io.Writer
を満たすので、オブジェクトを数珠つなぎにして利用することができます。
バッファリング機能の付与
GoのI/O処理は基本的にバッファリングを行わないことが多いようですが、下記の構造体を利用すると、バッファリング機能を付与することができるようになります。
バッファリングとは、入出力を指示されるたびに実際にオブジェクトとの読み書きを実行するのではなく、内部である程度バッファ領域を持っておいて、その分だけはバッファしたデータから入出力を行う仕組みのことです。何度も実際のオブジェクトとの入出力(例えば、OSのシステムコールの実行)をするとオーバーヘッドが発生してしまう場合がありますが、それを軽減することができます。
バッファリング機能は bufio
パッケージで提供されており、読み込み・書き込み・読み書き、それぞれの構造体が用意されています。デフォルトでは内部バッファは 4096 バイトですが、末尾に Size
が付いたファクトリ関数を使用すると、任意のバッファサイズを指定できます。
構造体 | ファクトリ関数 |
---|---|
bufio.Reader |
bufio.NewReader , bufio.NewReaderSize
|
bufio.Writer |
bufio.NewWriter , bufio.NewWriterSize
|
bufio.ReadWriter |
bufio.NewReadWriter , bufio.NewReadWriterSize
|
その他
その他にも、便利な機能が用意されています。
関数 | 説明 |
---|---|
io.MultiReader |
複数の io.Reader から順次読み込みを行う。 |
io.MultiWriter |
複数の io.Writer へ同時に書き込みを行う。 |
io.LimitReader |
指定したバイト数で強制的に読み込みを中断する(EOFを返す)。 |
io.TeeReader |
読み込みと同時に別のオブジェクトへの書き込みも行う。由来は tee コマンドと同じで、アルファベットの T の字形と思われる。 |
io.NopCloser |
io.Reader に何も行わない Close 関数を足して、io.ReadCloser を満たすオブジェクトに変換する。 |
io.NewSectionReader |
あらかじめ指定したセクション(任意のオフセット位置から任意のバイト数分のデータ)を読み込む。 |
Discussion