我一直遇到Scanner#nextLine的问题。根据我的理解,nextLine()应返回当前输入流的其余部分,然后继续下一行。
<img
src='img/1.jpg'
>
问题是最后一行。如果我将其保留为input.nextLine(),则循环的下一次迭代将接受该月的换行符。这是为什么?不应该在catch块中调用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());
}
}
在有人将此标记为重复之前,请理解我已经查看了stackoverflow上next和nextLine之间的差异。 nextLine应该使用换行符,但这里似乎没有这样做。感谢。
答案 0 :(得分:2)
if (input.hasNextInt() == true) { // prefer `if(input.hasNextInt())`
year = input.next();
} else {
throw new java.util.InputMismatchException();
}
输入badinput
的会将input.hasNextInt()
评估为false,这意味着else
块将在没有消费 badinput
的情况下执行(为此我们需要致电next()
- 而不是nextLine()
,因为您可能知道我们是否会在nextLine
之后使用nextInt
我们将使用剩余的行分隔符,而不是来自 next <的值/ em>喜欢,更多信息Scanner is skipping nextLine() after using next(), nextInt() or other nextFoo() methods)。
因为else块只是抛出异常,它将控制流移动到catch
部分。这意味着我们正在跳过break
所以我们的循环需要再次迭代。
现在在catch部分你只是打印
System.err.println(e);
System.out.println("\nOne of your inputs was not valid.");
System.out.println(input.nextLine());
打印异常e
,字符串"\nOne of your inputs was not valid."
和nextLine()
的结果(如前所述)将只消耗在最后nextInt()
次调用后保留的行分隔符,因此我们仍然没有从扫描仪中消耗badinput
。
这意味着当循环开始另一次迭代并请求月份时,它会收到无效batinput
的{{1}},因此int
会抛出nextInt()
。我们再次以InputMismatchException
块结束,我们致电catch
这次消耗nextLine()
。
现在,因为我们最终消耗了不正确的值循环将开始另一次迭代,我们将被要求月份的值。
要避免此类问题,请阅读以下示例:Validating input using java.util.Scanner。在第一个示例中,您将找到在提供时验证每个输入的方法
badinput
为避免多次编写此代码,请创建自己的实用程序方法。你甚至可以跳过你需要的条件,从数字到正面,如:
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();
}
您可以添加该实用程序方法的另一个版本,您可以让程序员添加应该传递的数字条件。我们可以使用IntPredicate功能接口添加到Java 8中,这将允许我们使用lambdas创建条件,如
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");
用法:
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;
}
答案 1 :(得分:1)
我怀疑当你输入两位数的年份时,你正在使用next()读取它,所以它只会读取下一个字符串。即使您输入2位数年份的值,它也会留下2由您的nextLine()新行或空值读取,之后的任何内容将被遗留,包括输入无效值时的新行或回车。所以你的nextLine()里面的catch只读取了部分无效输入,但保留了新的行或回车。当您希望提示出现读取月份时,会导致异常发生。你可以在每个nextInt()或next()之后放置nextLine()来解决问题。
答案 2 :(得分:1)
请记住,Scanner
没有看到您的打印语句,它只是将输入读作字符的流。作为用户,您一次一行输入这些字符对于扫描仪而言毫无意义。
因此,您键入8<ENTER>
(其中<ENTER>
表示您的操作系统的实际换行符)。在nextInt()
之后,8
已被消耗。
然后输入2<ENTER>
,进行待定输入<ENTER>2<ENTER>
。请记住,到目前为止只消耗了8
。 nextInt()
跳过空白并返回2
,从而消费 <ENTER>2
。
然后输入badinput<ENTER>
,进行待定输入<ENTER>badinput<ENTER>
。由于下一个标记不是有效的整数,因此抛出异常,并输入catch
块,您可以在其中调用nextLine()
。它消耗所有字符,包括第一个<ENTER>
,并返回之前的文本,即空字符串。
此时,badinput<ENTER>
在流中仍处于待处理状态,并在您回送时进行处理。
这是人们使用Scanner
的方式的主要缺陷之一。 nextInt()
不会消耗该行,只有令牌,剩下的就是排在后面。
使用Scanner
:
Please enter a month in numeric form
8 2 17
Please enter a day in numeric form
Please enter a two-digit year
因为用户在第一行输入了所有3个值,所以代码将获取值,但仍会打印下两个提示,即使这是不必要的。这很奇怪。
解决方案1:不要使用Scanner
。它太奇怪了。太容易使用,太容易误用,也很难正确使用。
解决方案2:在每个nextLine()
之后调用nextInt()
以在接受的值之后刷新(静默消耗)任何额外的文本。如果您这样做,示例将如下所示:
Please enter a month in numeric form
8 2 17
Please enter a day in numeric form
2
Please enter a two-digit year
17
第一行的<SPACE>2<SPACE>17<ENTER>
将被忽略。
如果您想要完整的错误处理,可以将逻辑扩展到if (! nextLine().trim().isEmpty()) {/*ERROR*/}
。