🔃

【Go】標準ライブラリの io.Reader/io.Writer 機能

2024/11/16に公開

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
    • 一種のファイルとして扱われるため両方のインターフェースを実装してはいるが、実際にはそれぞれ片方のみ可能。
  • 生成方法:
    • 最初から使用できる状態になっている。
    • os.Stdin 変数:標準入力。 ※読み込み専用
    • os.Stdout 変数:標準出力。 ※書き込み専用
    • os.Stderr 変数:標準エラー。 ※書き込み専用

ネットワーク(通信ソケット)

TCP/UDPなどのネットワーク通信に使用します。

  • 型: *net.Conn
  • 入出力:両方可能(io.Reader | io.Writer
  • 生成方法:
    • net.Dial 関数:通常の生成方法。
    • net.DialTimeout 関数:タイムアウトを指定したい場合。
    • net.FileConn*os.File*os.Conn として扱いたい場合。

一時的なバイト配列バッファ(読み書き両用)

[]byte 型の変数を io.Readerio.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
  • 生成方法:

パイプ連結

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