bufioで標準入力のバッファサイズを指定する(Go)
はじめに
Goを使ってAtCoderに参加した際、大きな入力を受け取る場面で思ったように処理が進まないことがありました。その原因を調べた結果、標準入力のバッファサイズに起因する可能性があるとわかり、標準入力を受け取る際にバッファサイズ指定を指定する方法について調べました。ここでは、Goの標準パッケージのbufioを使って標準入力のバッファサイズを指定する方法を紹介します。
bufioでバッファサイズを指定する
以下のコード例は、bufio.Scanner.Bufferメソッドを使って、スキャナのバッファサイズを指定する方法を示しています。
package main
import (
"bufio"
"os"
)
// スキャナを作成
var sc = bufio.NewScanner(os.Stdin)
func main() {
buf := make([]byte, 0, 64*1024) // 初期バッファを作成
sc.Buffer(buf, 1024*1024) // スキャナのバッファサイズを 1MB に設定
}
Scanner.Buffer の使い方
第一引数 buf
初期バッファとして使用する []byte 型のスライスです。このスライスの容量cap(buf)
に基づき、バッファが拡張されるかが決まります。
第二引数 max
最大バッファサイズを指定します。この値は、スキャン中にトークン(受け取るデータの単位)として処理できる最大サイズです。
公式ドキュメントの説明
公式ドキュメントには以下のような記載があります。
Buffer sets the initial buffer to use when scanning and the maximum size of buffer that may be allocated during scanning.
スキャン時に使用する初期バッファと、バッファが拡張される際の最大サイズを設定します。The maximum token size must be less than the larger of max and cap(buf).
最大トークンサイズはmax
またはcap(buf)
の大きい方以下でなければなりません。If max <= cap(buf), Scanner.Scan will use this buffer only and do no allocation.
max
がcap(buf)
以下の場合、初期バッファのみが使用され、追加のメモリ割り当ては行われません。
デフォルトのバッファサイズ
デフォルトではMaxScanTokenSize = 64 * 1024
と指定されています。このとき、文字列なら最大でおよそ64 * 1024 = 65536
文字のデータを受け取ることができます。
以下のコードのように、バッファサイズを1MBに設定することで、より大きなデータを受け取ることができるようになります。
sc.Buffer(buf, 1024*1024) // スキャナのバッファサイズを 1MB に設定
バッファサイズが足りないと
バッファサイズが不足すると、スキャンが失敗しエラーが発生します。以下のコードでバッファサイズが足りない例を見てみます。
package main
import (
"bufio"
"fmt"
"os"
)
// スキャナを作成
var sc = bufio.NewScanner(os.Stdin)
// 標準入力を受け取る
func scan() string {
sc.Scan()
return sc.Text()
}
func main() {
buf := make([]byte, 0, 4) // 初期バッファを作成(長さ0、容量4バイト)
sc.Buffer(buf, 4) // スキャナの最大バッファサイズを4バイトに設定
s := scan()
if err := sc.Err(); err != nil {
fmt.Fprintln(os.Stderr, "読み取りエラー:", err)
os.Exit(1)
}
fmt.Println(s)
}
実行結果
上記のコードで、4バイトを超える入力を与えると次のようなエラーが出力されます。
読み取りエラー: bufio.Scanner: token too long
原因
バッファサイズ(初期バッファと最大バッファサイズ)が入力トークンを収容できないため、スキャンが失敗します。
まとめ
- Buffer(buf, max) でバッファサイズを指定できる
- デフォルトのバッファサイズは64KB
- 入力がバッファサイズを超えると入力が失敗する
Discussion