Scanner is skipping nextLine() after using next()

2020-05-08 06:36发布

I am using the Scanner methods nextInt() and nextLine() for reading input.

It looks like this:

System.out.println("Enter numerical value");    
int option;
option = input.nextInt(); // Read numerical value from input
System.out.println("Enter 1st string"); 
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)

The problem is that after entering the numerical value, the first input.nextLine() is skipped and the second input.nextLine() is executed, so that my output looks like this:

Enter numerical value
3   // This is my input
Enter 1st string    // The program is supposed to stop here and wait for my input, but is skipped
Enter 2nd string    // ...and this line is executed and waits for my input

I tested my application and it looks like the problem lies in using input.nextInt(). If I delete it, then both string1 = input.nextLine() and string2 = input.nextLine() are executed as I want them to be.

19条回答
beautiful°
2楼-- · 2020-05-08 07:13

TL;DR Use scanner.skip("\\R") before each scanner.newLine() call, which is executed after:

  • scanner.next()
  • scanner.next*TYPE*() method.

Things you need to know:

  • text which represents few lines also contains non-printable characters between lines (we call them line separators) like

    • carriage return (CR - in String literals represented as "\r")
    • line feed (LF - in String literals represented as "\n")
  • when you are reading data from the console, it allows the user to type his response and when he is done he needs to somehow confirm that fact. To do so, the user is required to press "enter"/"return" key on the keyboard.

    What is important is that this key beside ensuring placing user data to standard input (represented by System.in which is read by Scanner) also sends OS dependant line separators (like for Windows \r\n) after it.

    So when you are asking the user for value like age, and user types 42 and presses enter, standard input will contain "42\r\n".

Problem

Scanner#nextInt (and other Scanner#nextType methods) doesn't allow Scanner to consume these line separators. It will read them from System.in (how else Scanner would know that there are no more digits from the user which represent age value than facing whitespace?) which will remove them from standard input, but it will also cache those line separators internally. What we need to remember, is that all of the Scanner methods are always scanning starting from the cached text.

Now Scanner#nextLine() simply collects and returns all characters until it finds line separators (or end of stream). But since line separators after reading the number from the console are found immediately in Scanner's cache, it returns empty String, meaning that Scanner was not able to find any character before those line separators (or end of stream).
BTW nextLine also consumes those line separators.

Solution

So when you want to ask for number and then for entire line while avoiding that empty string as result of nextLine, either

  • consume line separator left by nextInt from Scanners cache by
    • calling nextLine,
    • or IMO more readable way would be by calling skip("\\R") or skip("\r\n|\r|\n") to let Scanner skip part matched by line separator (more info about \R: https://stackoverflow.com/a/31060125)
  • don't use nextInt (nor next, or any nextTYPE methods) at all. Instead read entire data line-by-line using nextLine and parse numbers from each line (assuming one line contains only one number) to proper type like int via Integer.parseInt.

BTW: Scanner#nextType methods can skip delimiters (by default all whitespaces like tabs, line separators) including those cached by scanner, until they will find next non-delimiter value (token). Thanks to that for input like "42\r\n\r\n321\r\n\r\n\r\nfoobar" code

int num1 = sc.nextInt();
int num2 = sc.nextInt();
String name = sc.next();

will be able to properly assign num1=42 num2=321 name=foobar.

查看更多
疯言疯语
3楼-- · 2020-05-08 07:14

There seem to be many questions about this issue with java.util.Scanner. I think a more readable/idiomatic solution would be to call scanner.skip("[\r\n]+") to drop any newline characters after calling nextInt().

EDIT: as @PatrickParker noted below, this will cause an infinite loop if user inputs any whitespace after the number. See their answer for a better pattern to use with skip: https://stackoverflow.com/a/42471816/143585

查看更多
叼着烟拽天下
4楼-- · 2020-05-08 07:15

In order to avoid the issue, use nextLine(); immediately after nextInt(); as it helps in clearing out the buffer. When you press ENTER the nextInt(); does not capture the new line and hence, skips the Scanner code later.

Scanner scanner =  new Scanner(System.in);
int option = scanner.nextInt();
scanner.nextLine(); //clearing the buffer
查看更多
爷、活的狠高调
5楼-- · 2020-05-08 07:16
public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int i = scan.nextInt();
        scan.nextLine();
        double d = scan.nextDouble();
        scan.nextLine();
        String s = scan.nextLine();

        System.out.println("String: " + s);
        System.out.println("Double: " + d);
        System.out.println("Int: " + i);
    }
查看更多
姐就是有狂的资本
6楼-- · 2020-05-08 07:16

Why not use a new Scanner for every reading? Like below. With this approach you will not confront your problem.

int i = new Scanner(System.in).nextInt();
查看更多
仙女界的扛把子
7楼-- · 2020-05-08 07:18

In one of my usecase, I had the scenario of reading a string value preceded by a couple of integer values. I had to use a "for / while loop" to read the values. And none of the above suggestions worked in this case.

Using input.next() instead of input.nextLine() fixed the issue. Hope this might be helpful for those dealing with similar scenario.

查看更多
登录 后发表回答