5

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!

Houdini
  • 3,442
  • 9
  • 31
  • 47
  • I see in `findBestMove` that you do a `scan.close()` which will also close the source stream - do you do the same in `startGame()`, somewhere after your `etc`? – Andreas Fester Nov 14 '12 at 07:54
  • 1
    similar issue discussed in this question, not sure if it should be closed as a dupicate: http://stackoverflow.com/questions/7056749/scanner-issue-when-using-nextline-after-nextint – Denis Tulskiy Nov 14 '12 at 07:58
  • @Dennis I looked at that prior to posting, I do not think my issue is related but could be wrong. – Houdini Nov 14 '12 at 08:02
  • @Andreas yes I close the scanner in both cases. – Houdini Nov 14 '12 at 08:03
  • 1
    @Houdini then, this could be the second issue beside the one Denis mentioned. Dont do it - `InputStream` implements the `Closeable` interface, in which case `Scanner.close()` will also close `System.in`. http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html – Andreas Fester Nov 14 '12 at 08:06
  • Thanks Andreas, good to know anyway. But I close the scanner after I call `nextInt()`. – Houdini Nov 14 '12 at 08:09
  • do you close the scanner after `startGame`? – Denis Tulskiy Nov 14 '12 at 08:12
  • Yes, I included a bit more of the code to reflect that part. – Houdini Nov 14 '12 at 08:16
  • 1
    do you have a global `try...catch` block anywhere? because closing a scanner and opening a new one gives me an `NoSuchElementException` on next read because stdin is closed. – Denis Tulskiy Nov 14 '12 at 08:20
  • I do not, I could add one but I am not sure how catching a `NoSuchElementException` would help here. Maybe I am confused about what you are saying, but in the block of code that does not work, that is the only scanner used in the class. I open it, try to grab the first `int`, then close it again. – Houdini Nov 14 '12 at 08:23
  • this is confusing because nextInt can not return null, it can only throw an exception – Denis Tulskiy Nov 14 '12 at 08:30
  • I did not mean to imply it returns null. I fixed that, that was a poor choice of wording. It does throw an `NoSuchElementException`. I am just not sure why/how catching it would help..? – Houdini Nov 14 '12 at 08:33

4 Answers4

6

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();
Denis Tulskiy
  • 19,012
  • 6
  • 50
  • 68
5

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();
    }
}
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
  • 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 Nov 14 '12 at 16:40
  • 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. – Houdini Nov 14 '12 at 16:47
  • Thanks :) I have updated the sample code. In any case, when using static imports, make sure to review the [Static Import](http://docs.oracle.com/javase/1.5.0/docs/guide/language/static-import.html) 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 http://stackoverflow.com/questions/2504078/java-out-println-how-is-this-possible, in particular the answer from `Stephen C`. – Andreas Fester Nov 15 '12 at 07:00
0

Try using scan.reset(); instead of scan.close(). scan.close() will throw NoSuchElementException as Denis Tulskiy said.

chAmi
  • 1,774
  • 3
  • 20
  • 27
  • The exception is thrown before `scan.close()`. – Houdini Nov 14 '12 at 08:36
  • 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. – chAmi Nov 14 '12 at 08:43
  • Even if I leave the scanner open, I get the same result. – Houdini Nov 14 '12 at 08:47
0

I just spent about 15 minutes troubleshooting this issue and my problem was different than all of the above.

The code is fine, but I am using Sublime Text and running it in the terminal emulator inside sublime text, on windows. This prevented the scanner from actually allowing me to try to input something.

I ran the app using cmd and it worked fine. I hope this helps someone.

SgtPooki
  • 11,012
  • 5
  • 37
  • 46