【问题标题】:Scanner loop doesn't cycle when hitting enter twice按两次输入时扫描仪循环不循环
【发布时间】:2015-12-23 13:40:52
【问题描述】:
boolean loop = false;
double numberOfStudents;


System.out.print("Enter a number: ");

if ((scnr.nextLine().trim().isEmpty()) ) {
    loop = true;
}

while (loop) {
    System.out.println("Enter a number");

    if (scnr.hasNextDouble() ){
        System.out.println("Loop has stopped");
        numberOfStudents = scnr.nextDouble();
        loop = false;
    }

}           

System.out.println("You're outside the loop!");

我试图让程序说“输入一个数字”,直到用户输入一个实际数字(没有空格、字母或符号)。当用户输入一个数字时,它会将numberOfStudents 设置为等于该数字并跳出循环。

但是如果你按两次回车,它就不会迭代。它只显示一次“输入数字”。

循环逻辑有什么问题?为什么在获取有效输入之前它不循环?

【问题讨论】:

  • 我认为这不是一种方便的循环方式。
  • 当用户不提供双精度时,没有其他要求再次询问
  • 一开始不检查输入是否为数字
  • 空行不是scnr.hasNextDouble() 的有效令牌,因此它仍在等待...

标签: java while-loop java.util.scanner


【解决方案1】:

对于您的问题“为什么'输入数字'不显示多次?”的实际答案?见Tom's comment(更新:汤姆的回答)。

我已经以一种保留代码的方式重写了您的循环,但也使处理格式异常变得更容易一些(尽管有默默吞下异常的风险——对于这个用例来说应该是可以接受的)。

你可以使用这个设计,here is an SO post 为什么空的 catch 块可能是一个不好的做法。

public static void main(String args[])
{
    boolean loop = true;
    double numberOfStudents;
    Scanner scnr = new Scanner(System.in);

    while(loop){
        System.out.print("Enter a number: ");

        String input = scnr.nextLine();
        try{
            numberOfStudents = Double.parseDouble(input);
            loop = false;
        }catch(NumberFormatException e){
        }
    }
    System.out.println("You're outside the loop!");
}

输出:

输入一个数字:
输入一个数字:
输入一个数字:
输入一个数字:50
你在循环之外!

【讨论】:

    【解决方案2】:

    首先:由于您正在从System.in 读取数据,因此在用户输入有效令牌之前,对输入流的调用将被阻塞。

    所以让我们使用您的 scnr 变量检查第一次扫描:

    scnr.nextLine()
    

    nextLine() 读取 所有内容 直到下一行分隔符。所以如果你只是按return,那么它将成功读取它并执行下一个东西。

    下一个电话是:

    scnr.hasNextDouble()
    

    这个调用需要一个“真实的”标记并忽略空格,除了作为标记之间的分隔符。因此,如果您再次按下 return ,它实际上并不会读取该输入。所以它仍然等待更多(对于第一个令牌)。这就是为什么它卡在你的循环中并且你不会得到另一个“输入一个数字”输出。

    您可以通过输入一个真实的标记(如数字)或更改循环(如trobbins 所说的)来解决此问题。

    我希望你现在对你的程序流程有了更多的了解:)。

    【讨论】:

    • 现在确实很有意义。感谢您的帮助!
    【解决方案3】:

    虽然 trobbins 代码基本上可以解决您的问题,但使用异常进行流控制是一种不好的做法。

    我使用了一个小的正则表达式来检查值是否为数字。但是这个例子并不完整,它仍然会崩溃,例如用户输入两个小数点。因此,您需要创建一个适当的数字检查,或者只使用更容易检查的整数。

    cmets 中有人指出,人们可能想输入像5e10 这样的科学记数法,所以这也是要检查的另一种情况。如果这只是你需要的一些代码作为概念证明或一些快速而肮脏的东西,你可以使用异常处理方法,但在生产代码中你应该避免以这种方式使用异常。

    double numberOfStudents;
    Scanner scnr = new Scanner(System.in);
    
    while(true) {
        System.out.print("Enter a number: ");
        String input = scnr.nextLine().trim();
    
        if(input.matches("^[0-9\\.]{1,}$")) {
            System.out.println("Loop has stopped");
            numberOfStudents = Double.parseDouble(input);
            break;
        }
    }
    
    System.out.println("You're outside the loop!");
    

    【讨论】:

    • "hasNextDouble 不等待输入,它只是检查是否有任何输入。" 太糟糕了,这是错误的:(。
    • 您的解决方案解决了 OP 的问题,但在用户以科学记数法输入数字的罕见用例(即 1e10)上失败了。这仍然是一个很好的答案
    • @Trobbins 第一句也失败了:P,但是是的,其余的都可以。
    • 好吧,至少我认为它是这样工作的。看来我要读书了
    【解决方案4】:

    以下代码应该对您有所帮助:

        double numberOfStudents = 0;
        Scanner scnr = new Scanner(System.in);
    
        boolean readValue = false;            //Check if the valid input is received
        boolean shouldAskForNumber = true;    //Need to ask for number again? Case for Enter
        do {
            if (shouldAskForNumber) {
                System.out.print("Enter a number:");
                shouldAskForNumber = false;
            }
    
            if (scnr.hasNextDouble()) {
                numberOfStudents = scnr.nextDouble();
                readValue = true;
            } else {
                String token = scnr.next();
                if (!"".equals(token.trim())) {   //Check for Enter or space
                    shouldAskForNumber = true;
                }
            }
        } while (!readValue);
    
        System.out.printf("Value read is %.0f\n", numberOfStudents);
        System.out.println("You're outside the loop!");
    

    更新

    以不同的方式理解以下有问题的陈述:

    但是如果你按两次回车,它就不会循环回来。它只显示 “输入一个数字”一次。

    代码设置为仅在用户点击 RETURN/ENTER 或输入空格字符时打印一次“输入数字”。如果需要,您可以删除特殊检查并使用代码。

    【讨论】:

    • 这并不能解决 OP 的 current 问题。但它可以解决未来的问题。
    • 您更新的答案适用于数字和非数字输入,但不能解决 OP 对多个输入的担忧。每次在空白行上按回车键都应显示“输入数字:”
    • @Trobbins,弄错了!在此代码中,多次输入的考虑不同。 But if you hit enter twice, it doesn't loop back. It only displays "Enter a number" once. 让我觉得它不应该一次又一次地打印。 :D
    • 如果用户只是按“return”而不向控制台写入任何内容,是否意味着这段代码仍然会失败?
    • @Tom, fails 在这个意义上?
    【解决方案5】:
    import java.util.Scanner;
    
    public class Testing {
        public static boolean checkInt(String s)
        {
            try
            {
                Integer.parseInt(s);
                return true;
            } catch (NumberFormatException ex)
            {
                return false;
            }
        }
        public static void main(String[] args) {
            boolean loop = false;
            double numberOfStudents;
            Scanner scnr = new Scanner(System.in);
    
                String input = "";
    
                while (!(checkInt(input))) {
                    System.out.println("Enter a number");
                    input = scnr.nextLine();
                }
                numberOfStudents = Integer.parseInt(input);
    
            System.out.println("Number of students: " + numberOfStudents );
        }
    }
    

    //此代码运行良好,如果您想检查一下。 //在您的代码中,如果第一个输入是 int/double,则采用另一个输入;如果第一个输入不是数字,那么您已经提到要再次输入..

    【讨论】:

    • 好代码,当然更干净,可以用另一种方法进行验证。我唯一的批评是您保证测试将至少运行 2 次:一次是循环开始时,第二次是用户输入有效数字时。如果您可以在这种情况下只验证一次 input,您的代码会更好 - 仅当用户输入字符串时。
    • @trobbins 感谢您的宝贵意见。我发现你的代码非常高效和有用:)
    【解决方案6】:

    使用调试器查看代码实际在做什么。 Here's a guide on debugging in Eclipse。调试完代码后,你大概就知道问题出在哪里了。

    【讨论】:

    • 错了。 scnr.hasNextDouble() 不是假的,它会等到输入令牌并且它是否是双精度。当您测试代码时,您会注意到 :P
    • @Trobbins “一旦用户最终输入了一个数字,循环就会退出。” 或者如果你输入了一个无效的标记,比如一个文本,你就会遇到一个真正的问题,但是这个是另一个问题,不是这个问题的一部分:D.
    • 但我仍然对您如何测试 OPs 代码并实际获得无限循环感兴趣。你输入了什么或者只是按下了“return”?
    • ... 我强烈反对这实际上是一个完全可以通过调试完全理解的问题。您可能会看到 hasNextDouble 在您输入新行时不会触发,但这仍远未理解发生这种情况的根本原因以及发生的时间。
    • @Dukeling 我会支持这一点(“我强烈反对这实际上是一个完全可以通过调试完全理解的问题。”)。由于 djechlin 在该方法的行为上也犯了一个错误(而且他是一位经验丰富的 Java 开发人员(根据得分)),因此很难理解您是 Java 新手还是使用 Scanner 的新手。
    【解决方案7】:

    下面的代码会帮助你

        boolean loop = true;
        double numberOfStudents;
        Scanner scnr = new Scanner(System.in);
        System.out.print("Enter a number: ");
        String input = scnr.nextLine();
        while(!scnr.hasNextDouble()){
            System.out.print("Enter a number: ");
            try{
                numberOfStudents = Double.parseDouble(input);
                break;
            }catch(NumberFormatException e){
            }
            input = scnr.nextLine();
        }
        System.out.println("You're outside the loop!");
    

    【讨论】:

    • 如果 OP 只是按下“return”,而实际上没有向控制台写入内容,则此代码也会失败。
    • 当用户输入一串字母作为他们的第一个输入时,此代码将失败。同样,当按两次回车然后输入一些字母时,“输入一个数字:”会在同一行出现多次。
    【解决方案8】:

    以下代码正在运行,

    boolean loop = true;
        double numberOfStudents;
            Scanner scnr=new Scanner(System.in);
           while(loop) {
                System.out.println("Enter a number");
                   if ((scnr.nextLine().trim().isEmpty()) ) {
                loop = true;
            }
                if (scnr.hasNextDouble() ){
                    System.out.println("Loop has stopped");
                    numberOfStudents = scnr.nextDouble();
                    loop = false;
                }
            }
        System.out.println("You're outside the loop!");
    

    输出是,

    run:
    Enter a number
    hj
    po
    Enter a number
    lhf
    Enter a number
    o
    Enter a number
    p
    Enter a number
    a
    Enter a number
    34
    Loop has stopped
    You're outside the loop!
    

    【讨论】:

    • 我运行了你的代码,不幸的是它并没有改变 OP 的问题,即连续按下多个“输入”键不显示更多“输入数字”消息。
    • Troppins 说的是正确的。 OP的问题是他没有输入任何内容,只是按“返回”。您无法通过输入字母来重现该问题:P
    • 第一次输入数字时,您的代码也不起作用。
    • 是的.. 我的代码有一些问题。我正在努力解决这些问题。谢谢两位指出我的错误。我会改进我的答案。
    • 我同时使用 scnr.nextLine() 和 scnr.hasNextDouble。因此,当“ if ((scnr.nextLine().trim().isEmpty()) ) ”条件失败时,scnr,hasNextDouble() 需要在下一个循环中再输入一个..所以我认为唯一的解决方案是像在#Trobbins 帖子中一样将输入解析为 Double。
    【解决方案9】:

    如果您想再次从扫描仪中获取更多值,则必须扫描下一行。代码应该是这样的:

    while (loop) {
            System.out.println("Enter a number");
            if(!(scnr.nextLine().trim().isEmpty())){
                if (scnr.hasNextDouble() ){
                    System.out.println("Loop has stopped");
                    numberOfStudents = scnr.nextDouble();
                    loop = false;
                }
            }
        }
    

    【讨论】:

    • 请不要这样做。您在第一个 if 语句中丢弃了一行输入,而不管该行实际包含什么。并且不要将 nextLine 与其他 next... 函数混合使用 - 它们的交互效果不佳。
    • 与@Dukeling 提到的混合nextLine 和其他next 函数一起使用,这里是SO post explaining how this could break your expectations
    • 是的,你们都是对的。我做得太快了,只是为了表明他在逻辑上错过了一次扫描。我应该多加注意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-25
    • 1970-01-01
    • 1970-01-01
    • 2016-02-06
    • 2012-09-27
    • 2016-01-26
    相关资源
    最近更新 更多