Go 言語の標準入力について、自分用の備忘として残しておきます。
どこにでもある一般的な内容です。
<参考サイト>
・【Golang】fmt.Scanとbufio.Scannerの速度比較
・Go 言語で標準入力から読み込む競技プログラミングのアレ --- 改訂第二版
1. fmt ライブラリを使用した標準入力
まず、fmt.Scan
を使用して読み込む方法です。
簡単に使用できますが、大容量の入力を読み場合にはスピードが遅くなるなどの弱点もあります。
1-1. 基本的なサンプル
次のように、fmt.Scan(&格納先の変数)
とすれば、コンソールからの入力を取得できます。
&
は変数のアドレスを表します。
package main
import "fmt"
func main() {
var str string
fmt.Scan(&str) // データを格納する変数のアドレスを指定
fmt.Println(str)
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
123 456 789
123
fmt.Scan
を使用した場合、「改行」又は「スペース」までを1つの入力として読み込みます。
上記では、123 456 789
と入力していますが、最初のスペース区切りまでの 123
のみが取得されています。
1-2. スペース区切りの入力を読み込む場合
スペース区切りの入力を読み込むには、単純に、必要な回数分だけ読み込みを行います。
次のように、fmt.Scan
の引数にカンマ区切りで変数を指定することで、複数の入力を受けることができます。
package main
import "fmt"
func main() {
var n [3]int
fmt.Scan(&n[0], &n[1], &n[2])
fmt.Println(n)
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
123 456 789
[123 456 789]
上記のサンプルは、スペース区切りで3つの値があることを前提としていますが、値の数が未知の場合の対応はできません(未知の場合は、次項の bufio を使用します)。
なお、ここでは、入力を受け付ける変数に、int 型を使用しています。
fmt.Scan
では、入力値を望んだデータ型で受け取ることができますので、後でキャストする必要がなくなります。
<別の書き方>
必要な回数分だけ、入力を受け付ければ良いので、次のような書き方でも同じ結果を取得することができます。
複数行の入力を受け付ける場合も、これで対応できます。
func main() {
var n [3]int
for i := 0; i < 3; i++ {
fmt.Scan(&n[i])
}
fmt.Println(n)
}
2. bufio ライブラリを使用した標準入力
個人的には、この bufio
ライブラリの方が使い勝手が良いです。
ただし、文字列型(string
)で入力を取得するため、データ型を変更するなどの手間が生じます。
2-1. 基本的なサンプル
以下のように、まず、bufio.NewScanner
で、標準入力(os.Stdin
)を受け付けるスキャナを生成します。
あとは、scanner.Scan()
で1行分のスキャンを行い、scanner.Text()
で入力値を取得します(scanner
はただの変数なので、s
でも何でも構いません)。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin) // 標準入力を受け付けるスキャナ
scanner.Scan() // 1行分の入力を取得する
fmt.Println(scanner.Text())
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
abc 123 456
abc 123 456
bufio
を使用した場合、標準で「改行」を区切りとして入力を受け付けます。
上記のように、abc 123 456
というようにスペースを含めた入力をしても、そのまま1行分を取得します。
2-2. デフォルトサイズ(65,536文字)以上を読み込む
bufio
では、デフォルトで読み込む文字の最大サイズは MaxScanTokenSize
(65,536文字)に設定されます。
これ以上の文字数をスキャンできるようにするには、次のように、Buffer
関数(メソッド)を使用します。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Buffer(make([]byte, 64*1024), 100001) // 最大100,001文字
scanner.Scan()
fmt.Println(scanner.Text())
}
上の例は、最大の読み込み文字数を、100,001 文字に指定した場合となります。
Buffer
関数は次の構文で表されます。
func (s *Scanner) Buffer(buf []byte, max int)
buf
にはスキャン時に使用する初期バッファを指定します(コード中の 64*1024
が該当)。
max
にはスキャン中に割り当てられる可能性のあるバッファの最大サイズを指定します(コード中の 100001
が該当)。
2-3. スペース区切りの入力を個別に取得する
入力を1行ずつ取得した場合は、次のような形でスペース区切りで分割することで、それぞれの値を取得できます。
分割には、strings.Split(対象文字列, 区切り文字)
を使用しています。
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
inputs := strings.Split(scanner.Text(), " ")
for _, input := range inputs {
fmt.Println(input)
}
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
aaa bbb ccc ddd eee
aaa
bbb
ccc
ddd
eee
上の例では、aaa bbb ccc ddd eee
という入力を1行分取得した上で、スペース区切りで分解しています。
スペース区切りの数が未知の場合でも対応できます。
2-4. 複数行の入力を取得する
複数の入力を取得するには、その回数分 scanner.Scan()
を繰り返します(scanner
はただの変数なので、s
でも何でも構いません)。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
var n int = 3
var str []string
scanner := bufio.NewScanner(os.Stdin)
for i := 0; i < n; i++ {
scanner.Scan()
str = append(str, scanner.Text())
}
fmt.Println(str)
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
123
456
789
[123 456 789]
3. テキストファイルを読み込む
最後に、テキストファイルを読み込む方法も残しておきます。
準備として、main.go
と同じフォルダ内に次のテキストファイルを保存しておきます。
abcdefghijklmnopqrstuvwxyz
あいうえおかきくけこさしすせそ
3-1. 基本的なサンプル
まず、エラーが起こらない前提として簡単に書きます。
次のように、bufio.NewScanner
を使用することで、テキストファイルの内容が取得できます。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, _ := os.Open("test.txt") // ファイルを開く
scanner := bufio.NewScanner(file) // ファイルを読み取るスキャナー
for scanner.Scan() { // ファイルを1行ずつ読み込む(読み込む行が無かった場合はFalse)
fmt.Println(scanner.Text())
}
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
abcdefghijklmnopqrstuvwxyz
あいうえおかきくけこさしすせそ
3-2. エラー処理も含めたサンプル
エラー処理も含めると、次のようになります。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open(`C:\golang\sample\test.txt`)
if err != nil { // ファイルを開く際にエラーが生じた場合
fmt.Println(err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if scanner.Err() != nil { // 読み込み時にエラーがあった場合
fmt.Println(scanner.Err())
}
}