java - Scanner#nextLine() 留下剩余的换行符

标签 java java.util.scanner

我在使用 Scanner#nextLine 时遇到了问题。根据我的理解,nextLine()应该返回当前输入流的其余部分,然后继续到下一行。

while (true){
      try{
        System.out.println("Please enter a month in numeric form");
        month = input.nextInt();
        System.out.println("Please enter a day in numeric form");
        day = input.nextInt();
        System.out.println("Please enter a two-digit year");
        if (input.hasNextInt() == true){
          year = input.next();
        }
        else{
          throw new java.util.InputMismatchException();
        }
        break;
      }
      catch(Exception e){
        System.err.println(e);
        System.out.println("\nOne of your inputs was not valid.");
        System.out.println(input.nextLine());
      }
    }

问题出在最后一行。如果我将其保留为 input.nextLine(),则循环的下一次迭代接受月份的换行符。这是为什么? catch block 中对 nextLine 的调用不应该消耗该行的其余部分(包括换行符)并在下一次迭代中正确提示用户吗?注意:我决定打印它们来尝试弄清楚发生了什么,但没有雪茄。

我从终端收集了一些输出来说明我的意思:

// What should happen (this is when catch contains input.next() rather than nextLine)
    /*
    Please enter a month in numeric form
    8
    Please enter a day in numeric form
    2
    Please enter a two-digit year
    badinput
    java.util.InputMismatchException

    One of your inputs was not valid.
    badinput
    Please enter a month in numeric form <------------- prompts for input, as expected
    */

// What happens when I have nextLine in the catch block (code above)
    /*
    Please enter a month in numeric form
    8
    Please enter a day in numeric form
    2
    Please enter a two-digit year
    badinput
    java.util.InputMismatchException

    One of your inputs was not valid.
                                         <------------ leftover newline printed, as expected
    Please enter a month in numeric form <---------------- does not prompt for input due to another leftover newline (why?)
    java.util.InputMismatchException

    One of your inputs was not valid.
    badinput <----------------------- prints badinput even though it should've been consumed on the last iteration (why?)
    Please enter a month in numeric form
    */

在有人将其标记为重复之前,请理解我已经在 stackoverflow 上查看了 next 和 nextLine 之间的差异。 nextLine 应该使用换行符,但这里似乎没有这样做。谢谢。

最佳答案

if (input.hasNextInt() == true) { // prefer `if(input.hasNextInt())`
    year = input.next();
} else {
    throw new java.util.InputMismatchException();
}

对于输入 badinput 会将 input.hasNextInt() 评估为 false,这意味着 else block 将在不消耗的情况下执行/em>那个badinput(为此我们需要调用next() - 而不是nextLine(),因为你可能知道如果我们使用nextInt 之后的 nextLine 我们将使用剩余的行分隔符,而不是来自 next 的值,更多信息参见 Scanner is skipping nextLine() after using next(), nextInt() or other nextFoo() methods )。

因此,由于 else block 只是抛出异常,因此它将控制流移动到 catch 部分。这意味着我们正在跳过 break,因此我们的循环需要再次迭代。

现在在 catch 部分,您只需打印

System.err.println(e);
System.out.println("\nOne of your inputs was not valid.");
System.out.println(input.nextLine());

打印异常e,字符串“\n您的输入无效。”以及nextLine()的结果(如下所示)之前解释过)只会消耗上次 nextInt() 调用后剩余的行分隔符,因此我们仍然没有消耗来自 Scanner 的 badinput

这意味着当循环开始另一次迭代并请求月份时,它收到的 batinput 不是有效的 int,因此 nextInt() 抛出InputMismatchException。我们再次进入catch block ,并调用nextLine(),这次消耗了badinput

现在,由于我们最终消耗了错误的值循环,因此将开始另一次迭代,并且我们将被要求提供月份的值。

为了避免此类问题,请阅读以下示例:Validating input using java.util.Scanner 。在第一个示例中,您将找到在提供每个输入时验证每个输入的方法

Scanner sc = new Scanner(System.in);
int number;
do {
    System.out.println("Please enter a positive number!");
    while (!sc.hasNextInt()) {
        System.out.println("That's not a number!");
        sc.next(); // this is important!
    }
    number = sc.nextInt();
} while (number <= 0);
System.out.println("Thank you! Got " + number);

为了避免多次编写此代码,请创建您自己的实用程序方法。您甚至可以跳过需要数字为正数的条件,例如:

public static int getInt(Scanner sc, String askMsg) {
    System.out.println(askMsg);
    while (!sc.hasNextInt()) {
        System.out.println("That's not a number. Please try again");
        sc.next(); // consuming incorrect token
    }
    //here we know that next value is proper int so we can safely 
    //read and return it
    return sc.nextInt();
}

使用此方法,您的代码可以减少为

Scanner input = new Scanner(System.in);
int month = getInt(input, "Please enter a month in numeric form");
int day = getInt(input, "Please enter a day in numeric form");
int year = getInt(input, "Please enter a two-digit year");
<小时/>

您可以添加该实用程序方法的另一个版本,您可以在其中让程序员添加应该通过哪个数字的条件。我们可以使用 Java 8 中添加的 IntPredicate 函数接口(interface),这将允许我们使用 lambda 表达式创建条件,例如

public static int getInt(Scanner sc, String askMsg, IntPredicate predicate) {
    System.out.println(askMsg);
    int number;
    boolean isIncorrect = true;
    do {
        while (!sc.hasNextInt()) {
            String value = sc.next(); // consuming incorrect token
            System.out.println(value + " is not valid number. Please try again");
        }
        number = sc.nextInt();
        if (!predicate.test(number)) {
            System.out.println(number + " is not valid number. Please try again");
        }else{
            isIncorrect=false;
        }
    } while (isIncorrect);

    return number;
}

用法:

int year = getInt(input, "Please enter a two-digit year", i -> (i>=10 && i<=99));

关于java - Scanner#nextLine() 留下剩余的换行符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36108691/

相关文章:

java - 简单计算器 - "Resource leak: op is never closed"不关闭扫描仪有什么危害吗?

java - Java中Int后无法输入String

java - Spring中如何使用泛型?

java - 如何通过 runtime.exec() 在 java 中传递用户和密码

java - 如何使用 java 使用扫描仪类用字符串回答问题?

Java Scanner 抛出一些我没见过的错误

java - 在提供的字符串中查找各种子字符串

java - JCombobox 改变另一个 JCombobox

java - RuntimeEnvironmentPropertiesConfigurer 问题 - 在 tomcat 上运行 Broadleaf 管理时

java - JUnit-NoSuchElementException : No line found