web-dev-qa-db-ja.com

Java複数のスキャナー

以下に示すように、複数のIntegerオブジェクトを作成し、それらをLinkedListに入れるクラスがあります。

public class Shares<E> implements Queue<E> {
    protected LinkedList<E> L;

    public Shares() {
        L = new LinkedList<E>();
    }

    public boolean add(E price) {
        System.out.println("How many of these shares would you like?");
        Scanner scanInt;
        scanInt = new Scanner(System.in);
        Integer noShares = scanInt.nextInt();
        for (int i = 0; i < noShares; i++) {
            L.addLast(price);
        }
        scanInt.close();

        return true;
    }
}

コンソールから入力「add」をスキャンするアプリケーションがあり、見つかった場合は、以下に示すようにメソッドaddを呼び出します。

public class Application {
    private static Scanner scan;

    public static <E> void main(String[] args) {
        Queue<Integer> S = new Shares<Integer>();
        scan = new Scanner(System.in);
        System.out.println("Please type add");
        String sentence = scan.nextLine();
        while (sentence.equals("quit") == false) {
            if (sentence.equals("add")) {

                System.out
                    .println("What price would you like to buy your shares at?");

                S.add((Integer) scan.nextInt());

            } else
                System.exit(0);

            sentence = scan.nextLine();
        }
    }
}

アプリケーションは、ユーザーが何度でも「追加」を入力できるようにする必要がありますが、addメソッドが呼び出された後に「行が見つかりません」というエラーが表示されます。

これは、メソッドのScannerが閉じられておらず、必要に応じて再度開かれたためだと思います。これはプログラムのどこに問題があるのでしょうか。もしそうなら、どうすれば修正できますか?

これらの株式を販売する販売方法を追加するため、このプログラムは終了していません。そのため、whileループを使用しています。

12
Ryan Gibson

ストリームに対して複数のラッパーを用意することは、自分を本当に混乱させる優れた方法です。何をしているのか本当に理解していない限り、ストリームをラップするのは1回だけにすることをお勧めします。

これを行う最も簡単な方法は、別のシングルトンをラップするため、この場合はシングルトンを使用することです(最善の方法は、スキャナーとして引数として渡すことです)。

_public class Application { 
    // use this Scanner in all you other code, don't create another one.
    static final Scanner scan = new Scanner(System.in);

    public static <E> void main(String[] args) {
_

これは、メソッドのスキャナーが閉じられていないためだと思います

ストリームを閉じると、基になるストリームが閉じられ、再度使用することはできません。 System.inが再び使用されないようにする場合にのみ、System.inを閉じます。

どうすれば修正できますか?

最善の解決策は、すべてのスキャナーを1つの場所、1つのメソッド、または1つのクラスで使用することです。 main()でユーザーとのすべてのやり取りを行い、値をデータ構造に渡します。自分自身を初期化するオブジェクトを持つことは悪い習慣であり、これを始めると、残りの開発日の間悩まされます;)(深刻なことに、これが何度も何度も行われ、しばしば悪夢になります)


ところで、説明なしでプログラムを終了しないでください。エラーメッセージなしでSystem.exit(0);を呼び出すことも悪夢です。私はかつてSystem.exit()を260回呼び出すプロジェクトに取り組んだことがありましたが、エラーメッセージは頻繁に出されません。明らかな理由もなく停止しているサーバーを診断するのがどれほど楽しいか想像できます。

13
Peter Lawrey

最初の間違いは、このコード行が

scanInt.close();

scanIntオブジェクトだけでなく、System.inを閉じます。つまり、追加する最初の呼び出しの後、スキャンオブジェクトは既に持っている入力のみを消費し、NoSuchElementExceptionを受け取ります。この行を削除します。

今、あなたが持っている最後の行をこれで置き換えると

sentence = scan.nextLine();
System.out.println("sentence: \"" + sentence + "\"");

終了する前に取得する最後の入力は空の文字列であることがわかります。したがって、次のループでelseステートメントを入力すると、プログラムは実行を停止します。この問題を解決するには、以下を追加します。

scan.nextLine(); // consume the first always empty String...
System.out.println("Please type add");
sentence = scan.nextLine(); // and then get the actual value

ただし、複数のラッパーを使用しないでください。 Sharesクラスの請負業者に引数としてScannerオブジェクトを渡すことを検討してください。

6
nelly

私はこれが古い投稿であることを知っていますが、同じストリームで2つのScannerオブジェクトを使用しようとするとどうなるかを示す短い例を追加したいと思います。

public static void main (String [] args) {
    Scanner scan = new Scanner(System.in);
    Scanner scan2 = new Scanner(System.in);

    System.out.println("First: "+scan.nextLine());
    System.out.println("Second: "+scan2.nextLine());

    scan.close(); // Only one scanner is closed, but...

    System.out.println("Third: "+scan2.nextLine()); // Trying to use the other scanner will throw an exception
}
0
LAD

スキャナーは共有するストリームを消費するため、複数のスキャナーを(同じストリーム上に)置くのは非常に悪い習慣です。

ソースコードをデバッグし、見つかったScannerクラスを確認しながら確認しました。

  • ソース入力ストリームへの参照
  • 入力を保持するために使用される内部プライベートバッファ。

そのため、スキャナーインスタンスがストリームを消費するとき、基本的には、一連のバイト(1024)を読み取るだけで、ストリームの位置が先に移動します。

たとえば、nextLine()メソッドが呼び出されると、舞台裏でsource.read()が結果をプライベートバッファーにコピーします。

明らかに、他のスキャナーの状態が破損(無効)になります。

Javaソースコードを自分でデバッグするか、メソッド Scanner.readInput() を見てください。

0
freedev