【備忘】Go 言語の標準入力の基本的なこと

Go 言語の標準入力について、自分用の備忘として残しておきます。
どこにでもある一般的な内容です。

＜参考サイト＞
・[【Golang】fmt.Scanとbufio.Scannerの速度比較](https://scrapbox.io/tsuchinaga/%E3%80%90Golang%E3%80%91fmt.Scan%E3%81%A8bufio.Scanner%E3%81%AE%E9%80%9F%E5%BA%A6%E6%AF%94%E8%BC%83)
・[Go 言語で標準入力から読み込む競技プログラミングのアレ --- 改訂第二版](https://qiita.com/tnoda_/items/b503a72eac82862d30c6)

# 1. fmt ライブラリを使用した標準入力

まず、`fmt.Scan` を使用して読み込む方法です。
簡単に使用できますが、大容量の入力を読み場合にはスピードが遅くなるなどの[弱点](https://scrapbox.io/tsuchinaga/%E3%80%90Golang%E3%80%91fmt.Scan%E3%81%A8bufio.Scanner%E3%81%AE%E9%80%9F%E5%BA%A6%E6%AF%94%E8%BC%83)もあります。

## 1-1. 基本的なサンプル

次のように、`fmt.Scan(&格納先の変数)` とすれば、コンソールからの入力を取得できます。
`&` は変数のアドレスを表します。

```main.go
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` を使用した場合、「改行」又は「スペース」までを１つの入力として読み込みます。
上記では、`123 456 789` と入力していますが、最初のスペース区切りまでの `123` のみが取得されています。

## 1-2. スペース区切りの入力を読み込む場合

スペース区切りの入力を読み込むには、単純に、必要な回数分だけ読み込みを行います。
次のように、`fmt.Scan` の引数にカンマ区切りで変数を指定することで、複数の入力を受けることができます。

```main.go
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]
```

上記のサンプルは、スペース区切りで３つの値があることを前提としていますが、値の数が未知の場合の対応はできません（未知の場合は、次項の [bufio](#2-bufio-ライブラリを使用した標準入力) を使用します）。

なお、ここでは、入力を受け付ける変数に、int 型を使用しています。
`fmt.Scan` では、入力値を望んだデータ型で受け取ることができますので、後でキャストする必要がなくなります。

**＜別の書き方＞**
必要な回数分だけ、入力を受け付ければ良いので、次のような書き方でも同じ結果を取得することができます。
複数行の入力を受け付ける場合も、これで対応できます。

```main.go
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()` で１行分のスキャンを行い、`scanner.Text()` で入力値を取得します（`scanner` はただの変数なので、`s` でも何でも構いません）。

```main.go
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin) // 標準入力を受け付けるスキャナ
	scanner.Scan() // １行分の入力を取得する
	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` というようにスペースを含めた入力をしても、そのまま１行分を取得します。

## 2-2. デフォルトサイズ（65,536文字）以上を読み込む

`bufio` では、デフォルトで読み込む文字の最大サイズは `MaxScanTokenSize`（65,536文字）に設定されます。
これ以上の文字数をスキャンできるようにするには、次のように、`Buffer` 関数（メソッド）を使用します。

```main.go
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` 関数は次の[構文](https://pkg.go.dev/bufio#Scanner.Buffer)で表されます。

```go:func&#32;(*Scanner)&#32;Buffer
func (s *Scanner) Buffer(buf []byte, max int)
```

`buf` にはスキャン時に使用する初期バッファを指定します（コード中の `64*1024` が該当）。
`max` にはスキャン中に割り当てられる可能性のあるバッファの最大サイズを指定します（コード中の `100001` が該当）。

## 2-3. スペース区切りの入力を個別に取得する

入力を１行ずつ取得した場合は、次のような形でスペース区切りで分割することで、それぞれの値を取得できます。
分割には、`strings.Split(対象文字列, 区切り文字)` を使用しています。

```main.go
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` という入力を１行分取得した上で、スペース区切りで分解しています。
スペース区切りの数が未知の場合でも対応できます。

## 2-4. 複数行の入力を取得する

複数の入力を取得するには、その回数分 `scanner.Scan()` を繰り返します（`scanner` はただの変数なので、`s` でも何でも構いません）。

```main.go
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` と同じフォルダ内に次のテキストファイルを保存しておきます。

```:C&#58;\golang\sample\test.txt
abcdefghijklmnopqrstuvwxyz
あいうえおかきくけこさしすせそ
```

## 3-1. 基本的なサンプル

まず、エラーが起こらない前提として簡単に書きます。
次のように、`bufio.NewScanner` を使用することで、テキストファイルの内容が取得できます。

```main.go
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. エラー処理も含めたサンプル

エラー処理も含めると、次のようになります。

```main.go
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())
	}
}
```