【问题标题】:An infinite loop owing to recursion (introductory)递归导致的无限循环(介绍)
【发布时间】:2014-01-09 00:54:39
【问题描述】:

我试图编写一种方法,使人们能够通过 Scanner 输入整数,而不会因异常而导致程序崩溃。这是我最初拥有的:(这里,sc 是一个 Scanner 对象)

    public static int inputInt(String message) {                                            
    int returnval = 0;
    System.out.println(message);
    try {
        returnval = sc.nextInt();
    } catch (Exception ex) {
        System.out.println("You must enter an integer.");
        inputInt(message);
    }
    return returnval;
}

当我使用无效输入测试程序时,启动了一个无限循环,消息和“您必须输入一个整数”被打印了很多次,然后被 Eclipse 停止。 我使用以下代码解决了这个问题:

 public static int inputInt(String message) { 
    int returnval = 0;
    System.out.println(message);
    String valstring = sc.nextLine();
    try {
        returnval = Integer.parseInt(valstring);
    } catch (Exception ex) {
        System.out.println("You must enter an integer.");
        inputInt(message);
    }
    return returnval;
}

为什么第一种方法失败,而第二种方法失败?是否有更清洁的方法来实现这一点?

【问题讨论】:

  • 基本原因是这样的:如果你告诉扫描器寻找一个整数,它看到一个不属于整数的字符,它会抛出一个异常,但那个字符仍然存在在输入。在要求它重试之前,您必须做一些事情来强制扫描仪跳过它。 sc.nextLine()(请参阅 Snow 的 Doorknob 的回答)将完成这项工作。
  • 请注意,如果您使用nextInt(),输入中会留下一个换行符
  • @RafaEl 真的。但还要注意,如果下一个nextInt() 看到换行符,它将跳过它,因为nextInt() 会查看标记,而换行符等空白字符(默认情况下)不是标记的一部分。

标签: java recursion infinite-loop


【解决方案1】:

是的,有一种更简洁的方式:使用hasNextInt

public static int inputInt(String message) {
    System.out.println(message);
    while (!sc.hasNextInt()) {
        sc.nextLine(); // clear the bad input first

        System.out.println("You must enter an integer.");
        System.out.println(message); // why use recursion? just use a while loop
    }
    return sc.nextInt();
}

我所做的更改:

  • 使用hasNextInt,因此您不必使用异常
  • 添加了sc.nextLine();(问题的根源)
  • 只做一个while循环而不是递归(当你可以简单循环时为什么要递归?)
  • 已消除的临时变量
  • 使其更具可读性 (IMO)

【讨论】:

  • 这里有一个问题。此方法接受输入 5 5 5(三个五之间有空格)。这是我的程序有问题吗?
  • @Ayesha 那是因为hasNextInt simple 获取下一个标记(即 5)。如果您不想这样,请致电 nextLine 并测试它是否是有效的 int。
  • 这将通过类似于此处显示的内容来完成,对吗?stackoverflow.com/questions/5439529/…
【解决方案2】:

问题

Scanner.nextInt()具有抛出3个异常的能力:

1- InputMismatchException:当下一个项目不是 int 时抛出。
2- NoSuchElementException: 当没有下一个东西的时候。
3- IllegalStateException:如果Scanner 已关闭。

因此,当您输入非法值时,例如“abc”,最上面的代码会执行以下操作:

采用 "abc" 并尝试将其转换为 int。 (nextInt())。因为“abc”不是数字,所以它不能转换,并抛出一个InputMismatchException。但是,由于该方法没有成功完成,“abc”被留下作为下一个要读取的项目。因为抛出了异常,所以运行 catch 块内的代码。它打印出“您必须输入一个整数”。然后它再次调用自己,传递自己message。然后它初始化returnval 并从第一次运行中打印出相同的message,因为您再次将它传递给自己。然后你进入try 块,因为“abc”没有被成功读取并留在那里,扫描仪读取“abc”并尝试再次将其转换为int。当然,这样不行,循环又开始了。

解决方案

现在您知道您的问题是什么,您可以找出解决方案。我认为最优雅的方法是使用Scanner 方法hasNextInt()。这将返回一个boolean,让您知道下一个要读取的项目是否可以转换为int。例如,如果下一项是“abc”,则该方法将返回false。对于下一项“1”,该方法将返回true。所以,如果你像这样修改你的代码,它应该可以工作:

public static int inputInt(String message) {                                            
    int returnval = 0;
    System.out.println(message);
    while(!sc.hasNextInt()) {
        sc.nextLine();
        System.out.println("You must enter an integer.");
    }
    returnval = sc.nextInt();
    return returnval;
}

这段代码的作用:

1- 初始化returnval 并打印出message
2- 只要扫描仪没有要读取的int,就进入 while 循环。有效地“等待”输入下一个int
3-一旦它有一个int要读取,它就会读取int并将值保存在returnval中。
4- 它返回returnval 给调用者。

(缓慢而稳定地赢得比赛,大声笑。我似乎总是回答迟缓。也许是因为我写小小说作为答案......;))

【讨论】:

  • 太好了!我喜欢你的回答非常全面,不要改变你的风格:)
  • @Ayesha 谢谢。很高兴我能帮上忙!
  • 如何使用 hasNextInt() 以便考虑整个输入的有效性,而不仅仅是输入的第一个标记?
  • @Ayesha 在什么意义上?如果接下来要读取的几个字符是“365”,那么将读取的int(因为代码将验证它为int 并继续读取它)将为365。“令牌”可以被定义为在组中重要的字符串。在这种情况下,上述输入的标记是“365”,而不仅仅是单个字符“3”。
  • 输入 5 5 5 是我使用的那个 - 我希望这样的输入被拒绝。
猜你喜欢
  • 2015-02-12
  • 1970-01-01
  • 1970-01-01
  • 2016-10-06
  • 2017-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
相关资源
最近更新 更多