14

I've been racking my brain for a while trying to understand how Scanner works. So here's the code:

Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String p = sc.nextLine();
System.out.println(s);
System.out.println(p);
System.out.println(sc.hasNextLine());

What I expect:

Love is good  <- press ENTER
Love is blind <- press ENTER
Love is good  <- output
Love is blind <- output
false

What I have:

Love is good   <- I press ENTER
Love is blind  <- I press ENTER
Love is good   <- output
Love is blind  <- output
<- press ENTER
true <- output

What I do not understand:

  1. Instead of immediate printing this line - System.out.println(sc.hasNextLine()); - it makes me to press ENTER again
  2. It prints true instead of false while there no more lines or symbols

What I have read: I have read a dozen of stackoverflow answers about using hasNextLine() after nextInt() and about how nextInt() does not consume the final symbol in the line but I don't understand why, even though I don't use nextInt() in here, I still need to press ENTER one more time and why hasNextLine() is true.

1
  • It is easy to construct an example where there would be no next line: write the input into a text file, then pipe that text file into the java program via the command line. Since the end of the input would be reached, your program would output false. It is also possible to end the input in a manual session, on my Bash this works with Ctrl+D
    – amon
    Commented Jan 17, 2016 at 10:22

6 Answers 6

14

Instead of Scanner use BufferedReader like so:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

class Test {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        String s = reader.readLine();
        String p = reader.readLine();
        System.out.println(s);
        System.out.println(p);      
        System.out.println(reader.ready());
    }
}

For more reading into the matter, look here and here. As a rule of thumb, when you need parsing stuff (like nextInt) use Scanner, otherwise stick to BufferedReader. BufferedReader is a lot faster than Scanner too.

As for why you got that troublesome behavior, see the javadoc for Scanner.

The part that applies in your particular case is:

A scanning operation may block waiting for input.

What this means in your case is: after the first two lines are read by Scanner, the Scanner was waiting for input, caused by its method call sc.hasNextLine(). Then when Enter was hit, it got an input (hence it prints true, according to docs it returns true when there is input). As after the method call sc.hasNextLine() there are no more executable lines in your program, the program ends, giving the illusion that it required two enter hits.

4
  • Wow, thanks a lot! Never used that before! I'm asking that all to solve problems on UVa Online Judge and using BufferReader + parsing seems easier than using Scanner. Thank you. Commented Jan 17, 2016 at 11:30
  • Done it, thanks. It also seems a lot easier with no nextInt()-nextLine() issues. Commented Jan 17, 2016 at 11:55
  • but does this answer the original question?
    – Jasper
    Commented Jan 17, 2016 at 12:38
  • @Jasper Can you please tell me how the original question is not answered here, so that I can edit if I have to? Commented Feb 25, 2016 at 13:38
11

From the Scanner documentation (emphasis by me):

public boolean hasNextLine()

Returns true if there is another line in the input of this scanner. This method may block while waiting for input. The scanner does not advance past any input.

What's probably happening is that hasNextLine is blocking because it's waiting for input, which you give by pressing enter.

1
  • Thank you for your answer! Could you also please tell me how to fix that presuming that I need to use Scanner? Commented Jan 17, 2016 at 9:45
10

First of all you are using Scanner declared to System.in so this means you are asking .in to check if their is any more value so it waits for the user to input something and after you press enter it is obvious that it returns true.

So if you want to check s or p declare Scanner like this:

Scanner sc = new Scanner(s);

or

Scanner sc = new Scanner(p);

then check for sc.hasNextLine(); this should return false as much I know but for user input I would recommend to create another Scanner variable and then take values from the user and use sc for your hasNextline() thing.

I hope the very first logic helps you.

2
  • You are scanning the lines already read? Use System.in instead of s and p.
    – Top Sekret
    Commented Jan 17, 2016 at 10:09
  • But Brother that is the reason he is getting true as a answer when he wants to check is their is any more line and with System.in it is going to return true every time.
    – Rohan Gill
    Commented Jan 17, 2016 at 10:15
9

You are asking for three lines of input!

Standard input always has one more line coming until you close the terminal. On a Unix system, you can probably press Ctrl+D at the end, and it will return flase (= input closed).

The program would behave as expected if you had a finite input. But interactive input: how does the program know the user does not intend to input Shakespeare next?

The function hasNextLine() will wait for new input to arrive (or for the signal that there cannot be new input). This function is not for "did the user already input another line" but instead means "wait for the next line". So for practical purposes, hasNextLine() is equivalent to "is the user input still connected, and could it eventually contain another line".

Why do you need that last line? Just remove it!

2
  • I see you point :) The last line was for understanding how hasNextLine() works. As I've written above, I've started solving problems on UVa Online Judge and the grader is sooo picky about how you organise the input loop so I have to understand how input in Java works (I'm way too bad at C++ to do problem solving). Commented Jan 17, 2016 at 11:33
  • 1
    When the input comes from a file, you can expect hasNextLine to eventually be false (but you don't want to use this API it is too slow for this contest). Still, you may or may not have an extra line at the end. Commented Jan 17, 2016 at 16:14
6

You have the problem, that the Scanner is reading from the given InputStream which can be something like a file or in your case System.in.

As described in this answer a File has an end and the sc.hasNextLine() will return false. By using the stream you expect it not to have an end to be able to provide input data and therefore will block and wait for your next input.

Maybe you can think of some "exiting char" (here it is the '#'):

Scanner sc = new Scanner(System.in);
StringBuilder builder = new StringBuilder();
while (sc.hasNextLine()) {
    String line = sc.nextLine();
    if (line.equals("#")) {
        break;
    }
    builder.append(line + "\n");
}
System.out.println(builder.toString());
sc.close();
0

In your case, I debugged your example and saw that

System.out.println(sc.hasNextLine());

line waits for a new input. It is neither true nor false. It does not exist. That is why, it blocks the application. It does not matter what you entered (enter or any other button on the keyboard), it would give you true because you supplied a new input.

4
  • 2
    It's not "null" because that would print as null. It has not yet returned Commented Jan 17, 2016 at 10:20
  • Yes, I know. Probably, there is a bigger problem than being a null. I just wanted to share the value of this line, when it blocks. Commented Jan 17, 2016 at 13:35
  • The "value of this line" also is not null, but it does not exist. (A primitive boolean cannot be null, only object variables can be null pointers) Commented Jan 17, 2016 at 16:16
  • It's still incorrect. It does matter which keys are pressed. For example if you typed "abc" and then closed the pioe (e.g. via Ctrl+D in a unix terminal) it would return false . Commented Jan 17, 2016 at 23:25

Not the answer you're looking for? Browse other questions tagged or ask your own question.