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

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

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

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

Eclipse

Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

Q&A

解決済

3回答

6245閲覧

Javaで文字入力ができない

Tazusa

総合スコア41

Java

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

Eclipse

Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

0グッド

0クリップ

投稿2018/03/18 14:10

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

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

状況は以下の通りです。

文字を表示してください。といくつ表示しますか。が行間なく出力されてしまい、入力する場所がない状態になっています。
無理に入力すると、
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); } } } }

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

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

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

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

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

guest

回答3

0

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

一見便利に見える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/18 15:34

KSwordOfHaste

総合スコア18392

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

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

Tazusa

2018/03/20 08:35

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

0

ベストアンサー

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

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

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

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

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

plain

11\n

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

plain

1\n

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

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

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

投稿2018/03/18 15:32

編集2018/03/18 15:35
swordone

総合スコア20649

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

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

Tazusa

2018/03/20 08:24

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

2018/03/20 08:46 編集

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

2018/03/20 08:50

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

2018/03/20 09:33 編集

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

0

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

投稿2018/03/18 14:15

LouiS0616

総合スコア35658

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

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

退会済みユーザー

退会済みユーザー

2018/03/18 14:23

エラーの階層的に NumberFormatException をラッピングしてるやつだとおもが
LouiS0616

2018/03/18 14:24

nextInt → nextLine → nextInt と順に呼び出されているので、本来nextLineに読み取ってほしかった文字列がintにパースされようとしているのかと思います。
退会済みユーザー

退会済みユーザー

2018/03/18 14:47

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

2018/03/18 14:54

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

2018/03/20 08:38 編集

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問