33
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-09-11

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

<参考サイト>
【Golang】fmt.Scanとbufio.Scannerの速度比較
Go 言語で標準入力から読み込む競技プログラミングのアレ --- 改訂第二版

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

まず、fmt.Scan を使用して読み込む方法です。
簡単に使用できますが、大容量の入力を読み場合にはスピードが遅くなるなどの弱点もあります。

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 を使用した場合、「改行」又は「スペース」までを1つの入力として読み込みます。
上記では、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]

上記のサンプルは、スペース区切りで3つの値があることを前提としていますが、値の数が未知の場合の対応はできません(未知の場合は、次項の 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() で1行分のスキャンを行い、scanner.Text() で入力値を取得します(scanner はただの変数なので、s でも何でも構いません)。

main.go
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 関数(メソッド)を使用します。

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 関数は次の構文で表されます。

func (*Scanner) Buffer
func (s *Scanner) Buffer(buf []byte, max int)

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

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

入力を1行ずつ取得した場合は、次のような形でスペース区切りで分割することで、それぞれの値を取得できます。
分割には、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 という入力を1行分取得した上で、スペース区切りで分解しています。
スペース区切りの数が未知の場合でも対応できます。

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:\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())
	}
}
33
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?