我在使用 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/