Java Scanner does not wait for input

14,469

Solution 1

According to the java.util.Scanner javadoc, Scanner.close() closes the associated stream if this stream implements the Closeable interface. java.lang.System.in is an InputStream, which implements the Closeable interface. Hence, after calling Scanner.close() on a Scanner which is associated with System.in, the System.in stream is closed and not available anymore.

The following SSCCE works for me. I have removed some code from the question which is not relevant to the actual issue. Note that with this approach, while it works, Eclipse gives me the warning "Resource leak: 'scan' is never closed", so a better solution would be to use one Scanner instance only.

package com.example;

import java.util.Scanner;
import java.util.Set;
import static java.lang.System.out;

public class ScannerTest {
    int p = 0;
    String name = "Test";

    public void startGame() {
        out.println("Player1: 1 for dumb player, 2 for smart player, 3 for human player.");
        Scanner scan = new Scanner(System.in);
        p = scan.nextInt();

        out.println("Result1: " + p);

        out.println("Player2: 1 for dumb player, 2 for smart player, 3 for human player.");
        p = scan.nextInt();

        out.println("Result2: " + p);

        // scan.close();   // Do not close the Scanner to leave System.in open
    }

    public int findBestMove(Set<Integer> moves, Object /*Board*/ b) {
        out.println("Player " +name+ ", select a column from 1-7: ");
        Scanner scan = new Scanner(System.in);
        int move = scan.nextInt();
        // scan.close();   // Do not close the Scanner to leave System.in open

        out.println("Move: " + move);

        return 0;
    }

    public void run() {
        startGame();
        findBestMove(null, null);
    }

    public static void main(String[] args) {
        ScannerTest st = new ScannerTest();
        st.run();
    }
}

Solution 2

nextInt() does not discard \n in the stream, so the next call finds nothing. You have to skip it manually with Scanner.skip("\n") or Scanner.nextLine()

EDIT After figuring out that "skipping readInt()" meant that the code threw an exception. and with @Andreas help, here's an alternative solution. In java6 a new class Console has been added for providing a better interface to stdin. And it returns a Reader that doesn't care how much you close it. So the following snippet works just fine:

    Scanner fi = new Scanner(System.console().reader());
    System.out.println(fi.nextInt());
    fi.close();

    fi = new Scanner(System.console().reader());
    System.out.println(fi.nextInt());
    fi.close();
Share:
14,469
Houdini
Author by

Houdini

Currently work as a software developer at CABEM Technologies.

Updated on June 04, 2022

Comments

  • Houdini
    Houdini almost 2 years

    I have two blocks of code here. One scanner properly waits user input, and the other just blows right through it and calls nextInt() which returns a NoSuchElementException. Here is the block that works:

    public void startGame() {
       out.println("Player1: 1 for dumb player, 2 for smart player, 3 for human player.");
       Scanner scan = new Scanner(System.in);
       p = scan.nextInt();
    
       if (p == 1)
            p1 = new DumbPlayer("ONE");
       if (p == 2)
            p1 = new SmartPlayer("ONE");
       else 
           p1 = new HumanPlayer("ONE");
    
       out.println("Player2: 1 for dumb player, 2 for smart player, 3 for human player.");
       p = scan.nextInt();
    
       if (p == 1)
            p2 = new DumbPlayer("TWO");
       if (p == 2)
            p2 = new SmartPlayer("TWO");
       else 
            p2 = new HumanPlayer("TWO");
    
       scan.close();
    

    And here is the block that does not:

    public int findBestMove(Set<Integer> moves, Board b) {
    
        Set<Integer> set = new HashSet<Integer>();
    
        out.println("Player " +name+ ", select a column from 1-7: ");
        Scanner scan = new Scanner(System.in);  <--here it should wait for input, but does not!
        int move = scan.nextInt();   <-- NoSuchElementException
        scan.close();
    
        for (int x = 1; x <= 7; x++) {
            set.add(move);
            move += 7;
        }
        ....etc
    

    Both of these are separate classes, and are called from a main method in yet another class. Basically main() calls startGame(), which in turn calls the findBestMove() method of some Player class...which is where the non-working code resides. Are there times in the program where it is not appropriate to take input? I was under the impression that anytime I wanted user input, I could use this approach. Thanks!

  • Denis Tulskiy
    Denis Tulskiy over 11 years
    @Houdini: before the call to nextInt()
  • Houdini
    Houdini over 11 years
    Why do I have to do it in the second case and not the first?
  • Houdini
    Houdini over 11 years
    I tried that but it it either says no line found, or in the case of "\n" it says at java.util.Scanner.skip(Unknown Source).
  • Houdini
    Houdini over 11 years
    But the problem is that there is no entering of data. The program does not wait for input. It just calls nextInt() without waiting for input at the console.
  • Denis Tulskiy
    Denis Tulskiy over 11 years
    @Houdini: I see, sorry, I misunderstood your question.
  • Houdini
    Houdini over 11 years
    The exception is thrown before scan.close().
  • chAmi
    chAmi over 11 years
    Yes of course. I'd have add details. By saying scan.close() i mean by using that. Actually it throw this error when it scan for next item it found that scanner is closed. That's what i found by debugging.
  • Houdini
    Houdini over 11 years
    Even if I leave the scanner open, I get the same result.
  • Houdini
    Houdini over 11 years
    Thanks a lot, someone else mentioned that also but they didnt say dont close() the one from the class that was already working! That is so odd to me, dont I open the input stream back up when I declare the new Scanner? Oh well...work now, thanks!
  • Houdini
    Houdini over 11 years
    You probably know this, but for others maybe reading you can do a import static java.lang.System.* and this will allow to use just out.println() also.
  • Denis Tulskiy
    Denis Tulskiy over 11 years
    @Houdini: see my edit about an alternative solution to your problem.
  • Andreas Fester
    Andreas Fester over 11 years
    Thanks :) I have updated the sample code. In any case, when using static imports, make sure to review the Static Import docs. Generally I am avoiding wildcard imports. And I am not sure if I would prefer statically importing System.out over explicitly noting System.out, especially because java.lang.* is implicitly imported anyway. See also stackoverflow.com/questions/2504078/…, in particular the answer from Stephen C.
  • Nic
    Nic almost 11 years
    For some reason, System.console() is returning null, so when I try to call console.reader() it gives me a NullPointerException.