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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

1683閲覧

【Java】頑固な入力待ちをしたい

himejiy3

総合スコア77

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

1クリップ

投稿2018/02/15 14:56

まだ入門書を読破していないレベルですが、モヤモヤするので質問させてください。

やりたいことは、次のようなコードで
0か1が入力されるまでひたすらdoを回したいだけなんです。

java

1import java.util.Scanner; 2import java.util.InputMismatchException; 3 4class IntBlnRensyuu { 5 public static void main(String[] args) { 6 try(Scanner stdIn = new Scanner(System.in);) { 7 8 int x; 9 do { 10 System.out.print("0=true/1=false:"); 11 x = stdIn.nextInt(); 12 } while (x < 0 || x > 1); 13 14 boolean a; 15 if (x == 0) { 16 a = true; 17 } else { 18 a = false; 19 } 20 System.out.println("論理型変数に" + a + "を代入しました"); 21 22 } catch (InputMismatchException e) { 23 System.out.println("不正な入力値です"); 24 } 25 } 26} 27

しかしながら、これだと-1や2の入力ではループしますが
『a』や『1.5』を入力すると「不正な入力値です」を表示して終了してしまいます。
不正な入力があっても尚ループさせるには、どう修正するものなのでしょうか。

入門書の序盤はどれも、こういうのを気にしたら負けのようですが
私は気になるのでよろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

catchがwhileの外にあるのが原因です。例外が起これば何があろうと(対応する例外がある)catchに飛ぶため、whileから外れてしまいます。
ループを続けるためには、whileの中でcatchする必要があります。
ただし、今使っているtry-with-resourcesでcatchすると、catchする場合標準入力が閉じられてしまうため、別のtryを入れます。

java

1 int x = -1; // あらかじめwhileの継続条件を満たす値を入れておくと安全 2 do { 3 try { 4 System.out.print("0=true/1=false:"); 5 x = stdIn.nextInt(); 6 } catch (InputMismatchException e) { 7 System.out.println("不正な入力値です"); 8 } 9 } while (x < 0 || x > 1); 10

無限ループの原因(コメントを受けて)

試してみると、
「0=true/1=false:不正な入力値です」の永久ループになってしまいました・・

自分でも試してみると、aなどの不正入力をすると無限ループ発生。
無限ループしないように工夫してスタックトレースを2回出してみるも、
Integer.parseInt()のNumberFormatExceprionと違って
Scanner#nextInt()のInputMismatchExceptionは入力が何かはスタックトレースに乗らないらしい
そこでScanner#nextIntの説明を読むとこんな文言が。

入力の次のトークンをintとしてスキャンします。次に示す方法で、次のトークンを有効なint値に変換できない場合、このメソッドはInputMismatchExceptionをスローします。変換に成功すると、スキャナは一致した入力の先に進みます。

この太字部分は、裏を返せば「変換できない場合スキャナの位置は動かない」ということではないか。

となるとどういうことが起こるか。例えば"a"を入れた場合、

  1. nextInt()で数値変換できないため例外発生。
  2. スキャナの位置は動かないため、まだ入力には"a"が残った状態になる。
  3. ループによりこの状態で再びnextInt()が呼ばれるため、また"a"を変換しようとして例外発生。

これが永遠に繰り返されるわけだ。
これを防ぐためには、例外発生時に変換できなかった文字を飛ばす必要がある。

java

1do { 2 try { 3 System.out.print("0=true/1=false:"); 4 x = stdIn.nextInt(); 5 } catch (InputMismatchException e) { 6 System.out.println("不正な入力値です"); 7 stdIn.next(); // 変換できなかった文字列を飛ばす 8 } 9 10} while (x < 0 || x > 1);

ただこれだと不格好なので、nextInt()を使わず文字列で読み込んでparseInt()という方法も考えられる。

java

1do { 2 try { 3 System.out.print("0=true/1=false:"); 4 x = Integer.parseInt(stdIn.next()); 5 } catch (NumberFormatException e) { 6 System.out.println("不正な入力値です"); 7 } 8 9} while (x < 0 || x > 1);

ちなみに

java

1 boolean a; 2 if (x == 0) { 3 a = true; 4 } else { 5 a = false; 6 }

ここのコードは次の1行で片づけられます。

java

1 boolean a = (x == 0);

投稿2018/02/15 15:06

編集2018/02/16 02:33
swordone

総合スコア20649

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

himejiy3

2018/02/15 15:23

回答ありがとうございます。 試してみると、 「0=true/1=false:不正な入力値です」の永久ループになってしまいました・・ どこが間違っている可能性があるでしょうか・・
swordone

2018/02/15 15:55

無限ループの原因について回答を編集しました。 改めて調べてnextInt()の性質を知りました。
himejiy3

2018/02/16 12:32

おおぉー!これです!こう動くところを見たかったんです! ありがとうございました。これで心置きなく次の章に移れます。 「boolean~」のオマケも感謝です。 「なんか上手い書き方があった気がする」とは感じていたのですが、そのままにしていました。
guest

0

参考までに、himejiy3さんのアプローチとは他の方法を紹介します。

Java

1import java.util.Scanner; 2import java.io.IOException; 3 4class Main { 5 static int input_1_or_0(String message, Scanner sc) throws IOException { 6 while(true) { 7 System.out.print(message); 8 String input = sc.nextLine(); 9 10 if(input.equals("0")) return 0; 11 if(input.equals("1")) return 1; // return Integer.valueOf(input); も可 12 13 System.out.println("論理型変数に" + input + "を代入しました"); 14 } 15 } 16 17 public static void main(String[] args) { 18 try(Scanner sc = new Scanner(System.in)) { 19 System.out.println( 20 input_1_or_0("0=true/1=false: ", sc) 21 ); 22 } 23 catch(IOException e) { 24 e.printStackTrace(); 25 } 26 } 27}

実行例 Wandbox
こういうときは文字列のまま扱った方が楽ですね。

投稿2018/02/15 15:19

LouiS0616

総合スコア35658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

himejiy3

2018/02/16 12:05

回答ありがとうございます。 厳密に言うと、実はこの練習問題のおおもとが要求していたのは 「0か1の入力のみを待つ(他は出力しない)」「booleanを使う」「出力は『true』か『false』という文字」 という制限だったので、このコードの結果だとちょっとアレなのですが、 言わんとしていることは伝わりました。 私の疑問に対しては答えになっていますので大丈夫です。 こういう発想もあることを覚えておきます。
LouiS0616

2018/02/16 14:24

それならばこうすればいいのでは? if(input.equals("0")) return false; if(input.equals("1")) return true;
himejiy3

2018/02/16 14:36

それですと、true・falseのところで 「エラー: 不適合な型: booleanをintに変換できません」 と出ます。
LouiS0616

2018/02/16 14:39

まだメソッドは学習していないのですね。 戻り値の型がintだからです。booleanに変更してください。
guest

0

Ctrl-D の入力にも対処してみました。

java

1import java.util.Scanner; 2 3class IntBlnRensyuu { 4 public static void main(String[] args) { 5 Scanner stdIn = new Scanner(System.in); 6 7 // 0, 1, または Ctrl-D が有力されるまで ループする。 8 // ループを抜けたときに、x には次のように設定されている。 9 // 0 (0 が入力されたとき) 10 // 1 (1 が入力されたとき) 11 // -1 (Dtrl-D が入力されたとき) 12 int x; 13 while (true) { 14 System.out.print("0=true/1=false:"); 15 16 // CTRL-D が入力されたときの処理 17 if (!stdIn.hasNext()) { 18 x = -1; 19 break; 20 } 21 String line = stdIn.nextLine().trim(); 22 if (line.equals("0") || line.equals("1")) { 23 x = Integer.parseInt(line); 24 break; 25 } 26 27 System.out.println("不正な入力値です"); 28 } 29 30 if (x == -1) { 31 System.out.println("入力が終了されました。"); 32 } else { 33 boolean a = (x == 0); 34 System.out.println("論理型変数 a に" + a + "を代入しました"); 35 } 36 } 37}

投稿2018/02/17 07:23

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

himejiy3

2018/02/17 09:05

回答ありがとうございます。既にベストアンサー済みであることをお許しください。 Ctrl+DというのはLinuxでEOF…だったでしょうか?(違ったらすみません) 私はWidows畑どっぷりで馴染みが無く、 ブラウザの「お気に入りに追加」と、Excelの行コピーでしか使わない組み合わせなので 「何故Ctrl+D ?!」と戸惑ってしまいました。 というわけで、私の環境ではCtrl+Dによる反応はありませんでしたが コードの流れは分かりやすいので覚えておきます。 余談ですが、こちらのコードの場合、私のVSCode環境では 5行目で Resource leak: 'stdIn' is never closed の警告が出ます。(無視してコンパイル・実行は可能) 私はつい先日、入門書片手に「さぁ頑張るぞ」と、これと似たコードを本の通りに入力したところ、この警告が出て出鼻をくじかれた次第です。 どうにも気になり、Javaの基礎が出来ないうちから 先にcloseについて、そしてtry文を調べなくてはいけなくなりました。 そしてどうにかInputMismatchExceptionに到達しました。 何と言うか、 足し算を知らないまま掛け算の九九を覚えさせられている気分で大変でした。 それでも、例え「趣味のプログラム」に過ぎずとも 最終的には「お行儀の良いコード」「きれいなコード」を書けるようになりたいので、 単純なテキストエディタよりは勉強になって良いと思っております。
katoy

2018/02/17 10:29

Scanner stdIn = new Scanner(System.in); int x; ... を次のようにすれば、警告がでなくなると思います。 int x; try (Scanner stdIn = new Scanner(System.in)) { ... }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問