トークンを理解すれば、勝手に入力スキップしちゃうScanner.nextXxx()三兄弟と仲良くなれる気がする

Javaでコンソールアプリを作ると、ScannerクラスのnextXxx()を使って入力値を得るところがうまく制御できないことがある。なんか知らんけど、勝手に入力をスキップされて、処理が終わってしまったりして。

しれっと入力をスキップしてやり過ごす`Scanner`インスタンスに対して、「お願い、怠惰なScannerクラスさん、もっと働いて!! [^1]」とか思ったり思わなかったりして……知らんけど!!

そんなくだらないこと言うのはこれくらいにして、さて本題へ。

今回は特に
`Scanner.next();`
`Scanner.nextInt();`
`Scanner.nextLine();`
の違いに関して、トークン[^2]とバッファーというものに注目しながら、挙動をみていきます。

# よくあるお悩み: `nextInt()`や`next()`の直後に、`nextLine()`を呼ぶと思ったように動きません！

よく目にするのは、こんなコードの例。

```java
private static void tryToNextLineExample() {
    Scanner sc = new Scanner(System.in);

    System.out.println("年齢を入力: ");
    int age = sc.nextInt();
    System.out.println(age + "歳ですね。");

    System.out.println("ついでに名前も入力: ");
    String name = sc.nextLine();
    System.out.println(name + "さん、はじめまして！");

    sc.close();
}
```

これの実行結果はこんな感じになる。

```
// 出力結果
年齢を入力: 
200                 // 200と入力後にエンター押下
200歳ですね。        // 結果を表示
ついでに名前も入力:   // って聞いてるのに、入力する暇なく...
さん、はじめまして！  // 完
```

2つ目の名前の入力がスキップされてしまう。
名前を尋ねておきながら！名前を入力するスキを与えてくれないのだ！！
一応、礼儀として名前は尋ねるけれども、実際には年齢だけしか回答を受け付けないなんて。失礼な奴なんだ、Scannerって奴は!（個人の感想です）。

ちなみに、`nextInt()`のところを`next()`に(& ageの型をvarなどに)書き換えて実行しても、同じような事態が起こる。

```java
// 上記一部抜粋&書き換え
System.out.println("年齢を入力: ");
var age = sc.next();
System.out.println(age + "歳ですね。");
```

## 対策

どうしてこんなことが起きるのかという話の前に、まずは簡単に対策を。
`sc.nextInt()`もしくは`sc.next()`を呼び出したあと、`sc.nextLine()`で名前を聞く前に、`sc.nextLine()`をかます。それだけ。

```java
Scanner sc = new Scanner(System.in);

System.out.println("年齢を入力: ");
var age = sc.next();
System.out.println(age + "歳ですね。");
sc.nextLine(); // ここでかましてやる！！！

System.out.println("ついでに名前も入力: ");
String name = sc.nextLine();
System.out.println(name + "さん、はじめまして！");

sc.close();
```

```
// 出力結果
年齢を入力: 
200               　// 200と入力してからエンター
200歳ですね。　　　　　　　　　
ついでに名前も入力:  　　　　　
名無しの権兵衛　　　　// 名前入力後、エンター
名無しの権兵衛さん、はじめまして！
```

めでたしめでたし。

https://www.youtube.com/watch?v=_xqzmDyLWvs

https://qiita.com/circular/items/900f654a7adbe19cc9ae

## 理由

さて、ではなんでこんなことが起こるのか。それをちゃんと理解するために、まずはトークンをご紹介する。
ざっくりと言えば、`nextInt()`(&`next()`)と、`nextLine()`はの違いは、返す値の違いだ。

**`nextInt()`&`next()`は、トークンを返す。`nextLine()`は行を返す。**[^3]

じゃあ、トークンとは何か？

### トークンと空欄(white space)

トークンは、ひとまとまりの単語/数字を表す。そのトークンの区切りは、空欄(white space)だ。この空欄、スペースだけでなく、`\t`（タブ）や改行`\n`も含まれる [^4]。

|`Scanner.nextXxx()`|空欄(` `)に出くわした時|tab(`\t`)に出くわした時|enter(`\n`)に出くわした時|型に関する制約|
|-|-|-|-|-|
|`next()`|その直前までの入力値取得|(左に同じく)|(左に同じく)|特になし|
|`nextInt()`|その直前までの入力値取得後、`Interger.parseInt()`|(左に同じく)|(左に同じく)|入力値が`Integer`にcastできないと`InputMismatchException`を投げる|
|`nextLine()`|見過ごす=入力値に含める|(左に同じく)|その直前までの入力値取得|特になし|

ここにあるように、`nextLine()`はトークンガン無視です。改行まで待ってから、全部バッサリ狩っていうワイルドな奴なんだな！

### トークンとバッファー

また`Scanner`を理解するには、バッファーというのも大切だ。バッファーは言わば、読み取った値の一時保管所。
なんと、目に見えない`\n`まで保管してくれる凄腕なのだ。けれどもこの`\n`まで保管してしまうというのが、厄介でもある。
あとは`Scanner`クラスのメソッドも、呼び出されたらまずバッファーの残りを見に行く。もし何か残し物があれば、そちらを優先して読み込む。空だったら観念して、新しくユーザーからの入力を待つ=文字入力ができる状態になる。

さて。先ほどの、イケていない例を参考に、このバッファーの挙動を追っていこう。

```java
// うまくいかないver.
Scanner sc = new Scanner(System.in);

System.out.println("年齢を入力: ");
int age = sc.nextInt();
// ユーザーの挙動:             "200"と押したあとに、エンターを押す
// => バッファーに保存された値: "200\n"

// 保存されたバッファーからnextInt()が"200"だけを取得、(内部でIntegerにcast後) ageに代入
// => バッファーに保存された値: "\n"

System.out.println(age + "歳ですね。");

// この時点でバッファーに保存されている値: "\n"
System.out.println("ついでに名前も入力: ");
String name = sc.nextLine();
// まずはバッファーを読み込み、残った文字列を発見するnextLine()
// "\n"を見つけた瞬間、その直前までの値("")を迷わずnameに代入

System.out.println(name + "さん、はじめまして！");

sc.close();
```

```java
// うまくいくver.
Scanner sc = new Scanner(System.in);

System.out.println("年齢を入力: ");
int age = sc.nextInt();
// この挙動は上記と同様なので割愛

System.out.println(age + "歳ですね。");

// この時点でバッファーに保存されている値: "\n"
sc.nextLine();
// バッファーに残された"\n"を読み込んで、バッファーを空にする

// この時点でバッファーに保存されている値: (なし)
System.out.println("ついでに名前も入力: ");
String name = sc.nextLine();
// バッファーが空だったので、ユーザーからの入力を受け付ける
// エンター直前までの値を読み込み、nameに代入

System.out.println(name + "さん、はじめまして！");

sc.close();
```

### トークンの区切り文字

トークンの区切りは、上記で触れた空欄ならなんでもOK。その結果、いけてないver.だと入力値によっては、こんなことも起こり得る。

```
年齢を入力:
200                // "200"と入力後、tabキー、エンターキーを順に押下
200歳ですね。　     // この時点のバッファーの値: "\t\n"
ついでに名前も入力:
        さん、はじめまして！ // バッファー内の"\n"までの文字列="\t"だけnameに代入
```

### 理解のための、極端な事例

極端な事例だと、こんなことも可能。

```java
// 極端な事例
Scanner sc = new Scanner(System.in);

System.out.println("年齢 名前の順で入力し、最後にエンターを押してください（年齢と名前の間はスペースを入れてください！） 例:12 名無しの権兵衛");
var age = sc.next();
System.out.println(age + "歳ですね。");

String name = sc.nextLine();
System.out.println(name + "さん、はじめまして！");

sc.close();
```

```
// 極端な事例の実行結果
年齢 名前の順で入力し、最後にエンターを押してください（年齢と名前の間はスペースを入れてください
！） 例:12 名無しの権兵衛
200 名無しの権兵衛                 // ユーザーが"200 名無しの権兵衛"と入力
200歳ですね。                     // age = 200
 名無しの権兵衛さん、はじめまして！ // name = " 名無しの権兵衛"
```

---

さて、本当に怠惰だったのは、一体誰だったのか。そう、こういう細かいことを飛ばして適当にコード書いてた私だ。ごめんね`Scanner`クラスの`nextXxx()`三兄弟。

ということで、おあとがよろしいようで。


[^1]: 実際はもっと切羽詰まった感じになりますが、ほら、会社の名前で書いているものだから、それなりにオブラートに包んだつもり。

[^2]: ドキュメントとかだと出てくるけど、初心者向けの本だとあまり出てこない印象がある用語だなーと思っている（もしくは私がひどく流し読みしているか！？）。つまり、この単語の意味がさっぱりだと、ドキュメント読むにも詰まるんだよね。ということで、抑えておくといいのかなと思っている。

[^3]: [Scanner class](https://www.cs.utexas.edu/users/ndale/Scanner.html)
[^4]: [Chapter 11 – Input/Output and Exception Handling (p.17)](https://home.csulb.edu/~mopkins/cecs277/ch11.pptx)