質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.87%

Javaで文字入力ができない

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,046

Tazusa

score 39

任意の文字列か直角三角形を選んで好きな回数または段数表示するプログラムを作っています。

しかし入力された任意の文字列を入力する方に問題があります。
なぜこうなってしまうのか、また、解決策を教えていただきたいです。

状況は以下の通りです。

文字を表示してください。といくつ表示しますか。が行間なく出力されてしまい、入力する場所がない状態になっています。
無理に入力すると、
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at Hello.main(Hello.java:38)
というエラーが出ます。

String c = stdIn.nextLine();
が問題かと思い、String c = stdIn.next();でも試してみました。
しかし、いくつ表示するかの数を打ち込むと、同じエラーが出ます。

なぜこうなってしまうのでしょうか。また、解決策を教えていただきたいです。
よろしくお願いします。

import java.util.Scanner;

public class Hello{

    static void reader(String c, int n) {

        for (int i = 1; i <= n; i++){
        System.out.println(c);
        }

    }

    static void triangle(int m) {
        for(int i = 1; i <= m; i++) {
            for(int j = 1; j <= i; j++) {
                System.out.print("*");
            }
            System.out.println();
        }

    }

    public static void main(String[] args) {
        try(Scanner stdIn = new Scanner(System.in)){

        int choose;
        do {
        System.out.println("文字表示なら1、三角形なら2");
        choose = stdIn.nextInt();
        }while (choose != 1 && choose != 2);

        if(choose ==1){
        System.out.println("文字を入力してください。");
        String c = stdIn.nextLine();

        System.out.println("いくつ表示しますか。");
        int n = stdIn.nextInt();

         reader(c,n);
        }

        else {
            System.out.println("何段表示しますか?");
            int m = stdIn.nextInt();

            triangle(m);
        }

        }
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+4

基本的にはLouiS0616さんの回答の通りですが、補足します。

「無理に入力」というのは、

文字を入力してください。
いくつ表示しますか。

と行間なく表示された後、表示したい「文字」を入力しようとした、ということではないでしょうか?
これを仮定して、ここで起こっている現象を解説します。

最初のchoose = stdIn.nextInt()で、「1」を入力してEnterを押したとします。
すると標準入力は

1\n


のように、末尾に改行文字を含んだ状態で保持します。
これでnextInt()が処理されると、「1」だけ拾って1を数値化してchooseに代入します。
そうなると標準入力は

\n


のように、改行文字だけが残る状態になります。
次にchooseが1である場合の処理で、「文字を入力してください」と表示され、nextLine()が実行されます。
ここで注意してほしいのは、標準入力にまだ文字が残っているということです。
この場合、入力待ちにはならず、残った文字を取り込もうとします。
nextLine()は、次の行の先頭まで読み進め、読み進めた文字列の最後の改行文字を除いた部分を返すメソッドです。今残っているのは改行文字だけなので、そこまで読み取り、改行文字を破棄して、空文字""が返されます。これがString cに代入されます。
そして次の「いくつ表示しますか。」が表示され、nextInt()の入力待ち(今度は文字が残っていないため)となります。

もうお気づきでしょうか。実は、行間なく2つのメッセージが表示された後は、実は「表示回数を入力するよう要求されているタイミング」だったのです。ここに文字を入れようとしたため、数字として解釈できない文字を受け取ったScannerは例外を発生させたのです。

これを防ぐには、最初の入力の時点で改行まで読み込んでしまう必要があります。
それが、Integer.parseInt(stdIn.nextLine())です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/20 17:24

    詳細に答えていただきありがとうございます。
    はい、おっしゃる通り行間なく表示された後に入力しようとしました。
    あまり理解していないまま使っていましたが、順を追って説明していただき理解できました。

    キャンセル

  • 2018/03/20 17:46 編集

    追加で申し訳ないのですが、Int型のものを読み込みたいときにいつもInteger.parseInt(stdIn.nextLine());
    を使うので問題があるのでしょうか?
    改行まで読み込んでくれるならそのほうが都合のいい場合も(今までは)多かったのですが・・・

    キャンセル

  • 2018/03/20 17:50

    各行に数値として扱える文字列しか入っていないなら問題ないです。
    数字以外の文字が入る場合はもちろんのこと、空白で区切られている場合も例外が発生しますので注意が必要です。

    キャンセル

  • 2018/03/20 18:33 編集

    注意すべきことが多いものなんですね。ありがとうございます。

    キャンセル

+4

nextIntの後にnextLineを使うと改行文字を読み取ってしまい、そのようなエラーが起きます。
choose = Integer.parseInt(stdIn.nextLine());などの処理に変えてみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/18 23:47

    うは・・・ソースをおってみたら nextLine で終端フラグがたってしまって nextXxx が終端過ぎてるから読み込めないよだった・・・
    なんじゃこの仕様

    キャンセル

  • 2018/03/18 23:54

    やっぱりnextInt他とnextLineは混ぜるな危険ってことですね。

    キャンセル

  • 2018/03/20 17:19 編集

    いつも素早い回答ありがとうございます。
    もっと勉強していきたいと思います。

    キャンセル

+4

直接的な回答とはいえませんが・・・

一見便利に見えるjava.util.Scannerですが、期待外の入力があったときの処理の仕方に若干ややこしい点があるように思えます。

なるべく混乱を避けてScannerを使おうとするなら、個人的に次のようなパターンで使うといいんじゃないかと感じます。

  • 推奨パターン1
    行がどこで分割されているかは気にしなくてよい場合の方法。文字列の読み込みはnext(), 文字列以外の読み込みはnextInt()などを使う。nextLine()は決して使わない。全ての入力が「正しい」と想定できる場合、言い換えれば期待外の入力(nextInt()に対して整数以外を入力とか)の場合はプログラム全体が異常終了してもかまわないという想定で使う。

  • 推奨パターン2
    行がどこで分割されているかは気にしなくてよい場合の方法。next()しか使わない。読み込めるのはデリミターで区切られた個々の文字列。整数などの数値もあくまで文字列表現として読み込む。期待するデータが文字列以外の物(整数や実数あるいは日付など)の場合は読み込んだ文字列に対してしかるべきメソッドで望むデータ型へ変換する。変換エラーが起きたらnext()で次の文字列を読み込むことでエラー回復を試みる。デバッグの際に「次に読み込まれる文字列が何か」が把握しやすい。

  • 推奨パターン3
    nextLine()しか使わない。読み込めるのは必ず行単位になる。空白やカンマで区切った複数のデータを読みたいならString#splitでまず文字列を分割する。その後、文字列以外の物を期待するなら推奨パターン2と同様にしてデータ型の変換をする。
    推奨パターン1,2と異なり、今入力したのが何行目なのかを把握することが容易になる。この情報は実用的なプログラムを書く際に使うことも多い。例えば期待外のデータが入力されたときにエラーメッセージに行番号を必要とする場合など。このパターンを使うならもはやScannerではなくBufferedReaderでよい気もする。


Scannerの振舞いについて曖昧な理解のままnext(), nextInt(), nextLine()を混在して使おうとしても混乱することが多いように思います。特にプログラムを覚え始めの方にとって「字句解析を伴うリーダー」は動作の把握が難しく感じるのではないでしょうか。

ライブラリーの振る舞いについて調べたり考えたりすることができるような経験が積めて来たら、改めて「Scannerってこんな風に振舞うんだなぁ」を理解してより複雑な使いかたに挑戦するとよいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/20 17:35

    ありがとうございます。
    まだまだ初心者なのでどうすればよいかわからないことだらけですが、大変勉強になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.87%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る