17

Sorry if this sounds too simple. I'm very new to Java.

Here is some simple code I was using to examine hasNextLine(). When I run it, I can't make it stop. I thought if you didn't write any input and pressed Enter, you would escape the while loop.

Can someone explain to me how hasNextLine() works in this situation?

import java.util.*;

public class StringRaw {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLine()) {
            String str = sc.nextLine();
        }
        System.out.print("YOU'VE GOT THROUGH");
    }
}
skaffman
  • 381,978
  • 94
  • 789
  • 754
Helgi
  • 328
  • 2
  • 3
  • 10

5 Answers5

18

When reading from System.in, you are reading from the keyboard, by default, and that is an infinite input stream... it has as many lines as the user cares to type. I think sending the control sequence for EOF might work, such as CTL-Z (or is it CTL-D?).

Looking at my good-ol' ASCII chart... CTL-C is an ETX and CTL-D is an EOT; either of those should work to terminate a text stream. CTL-Z is a SUB which should not work (but it might, since controls are historically interpreted highly subjectively).

Lawrence Dol
  • 59,198
  • 25
  • 134
  • 183
  • Yes -- the user just hasn't entered those lines yet. BTW guys, does the implementation block on .hasNextLine() or .nextLine()? – Dilum Ranatunga Apr 13 '11 at 17:47
  • Thanks alot guys. Only needed the CTRL-Z trick. I couldn't find any text about it. – Helgi Apr 13 '11 at 18:03
  • from java doc: hasNextLine 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. – Brian Sweeney Aug 10 '11 at 18:43
8

CTRL-D is the end of character or byte stream for UNIX/Linux and CTRL-Z is the end of character or byte stream for Windows (a historical artifact from the earliest days of Microsoft DOS).

With the question code as written, an empty line won't exit the loop because hasNextLine() won't evaluate to false. It will have a line terminator in the input byte stream.

System.in is a byte stream from standard input, normally the console. Ending the byte stream will therefore stop the loop. Although nextLine() doesn't block waiting for input, hasNextLine() does. The only way the code terminates, as designed, is with CTRL-Z in Windows or CTRL-D in UNIX/Linux, which ends the byte stream, causes hasNextLine() not to block waiting for input and to return a boolean false which terminates the while loop.

If you want it to terminate with an empty line input you can check for non-empty lines as part of the loop continuation condition. The following code demonstrates how to change the basic question design that uses hasNextLine() and nextLine() to one that terminates if it gets an empty line or an end of input character (i.e. CTRL-Z in Windows or CTRL-D in UNIX/Linux). The additional code in the while condition uses a feature of assignment operators wherein they can be evaluated like an expression to return the value that was assigned. Since it is a String object, the String.equals() method can be used with the evaluation.

Other additional code just adds some printed output to make what is going on obvious.

// HasNextLineEndDemo.java
import java.util.*;

public class HasNextLineEndDemo {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // this code is a bit gee-whiz
        // the assignment expression gets assigned sc.nextLine()
        // only if there is one because of the &&
        // if hasNextLine() is false, everything after the &&
        // gets ignored
        // in addition, the assignment operator itself, if
        // executed, returns, just like a method return,
        // whatever was assigned to str which, 
        // as a String object, can be tested to see if it is empty
        // using the String.equals() method
        int i = 1; // input line counter
        String str = " "; // have to seed this to other than ""
        System.out.printf("Input line %d: ", i); // prompt user
        while (sc.hasNextLine() && !(str = sc.nextLine()).equals("")) {
            System.out.printf("Line %d: ", i);
            System.out.println("'" + str + "'");
            System.out.printf("Input line %d: ", ++i);
        } // end while
        System.out.println("\nYOU'VE GOT THROUGH");
    } // end main
} // end class HasNextLineEndDemo
Jim
  • 2,552
  • 1
  • 18
  • 14
6

Hit Ctrl + D to terminate input from stdin. (Windows: Ctrl + Z) or provide input from a command:

echo -e "abc\ndef" | java Program
user unknown
  • 32,929
  • 11
  • 72
  • 115
1

I had a similar problem with a socket input stream. Most solutions I found would still block the execution. It turns out there is a not-blocking check you can do with InputStream.available(). So in this case the following should work:

int x = System.in.available();
if (x!=0) {
    //Your code
}
svikramjeet
  • 1,192
  • 8
  • 19
SimNa
  • 11
  • 2
0

As per my understanding , if you take an example of result set object from JDBC or any iterator then in these cases you have a finite set of things and the iterators each time check whether end of the set has been reached. However in the above case , their is no way of knowing the end of user input i.e. hasNextLine() has no way of knowing when user wants to terminate, and hence it goes on infinitely. Best way is to put additional condition on the for loop that checks for some condition inside for loop that fails in the future. In the above post @Jim 's answer illustrates this.

In fact using hasNextLine() as loop terminator for console input should be discouraged because it will never return false.

rootExplorr
  • 530
  • 3
  • 16