当前位置: 首页 > 知识库问答 >
问题:

如何使用java.util.Scanner从System.In正确读取用户输入并对其起作用?

孔彭祖
2023-03-14

这是一个可以用作重复目标的规范问题/答案。这些需求基于每天发布的最常见的问题,并可能根据需要添加到。它们都需要相同的基本代码结构来到达每个场景,并且它们通常相互依赖。

大多数scanner问题都包括在以上一个操作中失败的尝试。

>

  • 我希望能够让我的程序自动等待下一个输入后,每个先前的输入以及。

    我想知道如何检测一个退出命令,并结束我的程序时,该命令输入。

    在Java世界中,scanner是一个特例,它是一门极其挑剔的课,老师不应该给新学生指导使用。在大多数情况下,教师甚至不知道如何正确地使用它。它几乎没有用在专业的生产代码中,所以它对学生的价值是非常值得怀疑的。

    使用扫描器暗示了本问答中提到的所有其他内容。它绝不仅仅是关于扫描器的问题,而是关于如何用扫描器解决这些常见的问题,这些问题在几乎所有让扫描器出错的问题中总是共同的病态问题。从来不只是关于next()nextline()的问题,这只是类实现挑剔的一个征兆,在关于scanner的问题中发布的代码中总是有其他问题。

    答案显示了在StackOverflow上使用和询问scanner的99%情况的完整、惯用的实现。

    特别是在初学者代码中。如果您认为这个答案过于复杂,那么就向指导老师投诉,指导老师告诉新学员在解释其行为的复杂性、怪癖、不明显的副作用和特性之前使用扫描器

    scanner是一个很好的教学时刻,让我们了解最小惊异原则是多么重要,以及为什么一致的行为和语义在命名方法和方法参数中是很重要的。

    您可能永远不会真正看到scanner在专业/商业应用程序中使用,因为它所做的每一件事都被其他东西做得更好。现实世界的软件必须比scanner允许您编写代码更有弹性和可维护性。真实世界的软件使用标准化的文件格式解析器和文档化的文件格式,而不是在独立作业中给出的特殊输入格式。

  • 共有1个答案

    羊舌自强
    2023-03-14

    以下是如何正确使用java.util.scanner类以交互方式正确读取System.in中的用户输入(有时也称为stdin,特别是在C、C++等语言以及Unix和Linux中)。它惯用地演示了请求完成的最常见的事情。

    package com.stackoverflow.scanner;
    
    import javax.annotation.Nonnull;
    import java.math.BigInteger;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.*;
    import java.util.regex.Pattern;
    
    import static java.lang.String.format;
    
    public class ScannerExample
    {
        private static final Set<String> EXIT_COMMANDS;
        private static final Set<String> HELP_COMMANDS;
        private static final Pattern DATE_PATTERN;
        private static final String HELP_MESSAGE;
    
        static
        {
            final SortedSet<String> ecmds = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
            ecmds.addAll(Arrays.asList("exit", "done", "quit", "end", "fino"));
            EXIT_COMMANDS = Collections.unmodifiableSortedSet(ecmds);
            final SortedSet<String> hcmds = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
            hcmds.addAll(Arrays.asList("help", "helpi", "?"));
            HELP_COMMANDS = Collections.unmodifiableSet(hcmds);
            DATE_PATTERN = Pattern.compile("\\d{4}([-\\/])\\d{2}\\1\\d{2}"); // http://regex101.com/r/xB8dR3/1
            HELP_MESSAGE = format("Please enter some data or enter one of the following commands to exit %s", EXIT_COMMANDS);
        }
    
        /**
         * Using exceptions to control execution flow is always bad.
         * That is why this is encapsulated in a method, this is done this
         * way specifically so as not to introduce any external libraries
         * so that this is a completely self contained example.
         * @param s possible url
         * @return true if s represents a valid url, false otherwise
         */
        private static boolean isValidURL(@Nonnull final String s)
        {
            try { new URL(s); return true; }
            catch (final MalformedURLException e) { return false; }
        }
    
        private static void output(@Nonnull final String format, @Nonnull final Object... args)
        {
            System.out.println(format(format, args));
        }
    
        public static void main(final String[] args)
        {
            final Scanner sis = new Scanner(System.in);
            output(HELP_MESSAGE);
            while (sis.hasNext())
            {
                if (sis.hasNextInt())
                {
                    final int next = sis.nextInt();
                    output("You entered an Integer = %d", next);
                }
                else if (sis.hasNextLong())
                {
                    final long next = sis.nextLong();
                    output("You entered a Long = %d", next);
                }
                else if (sis.hasNextDouble())
                {
                    final double next = sis.nextDouble();
                    output("You entered a Double = %f", next);
                }
                else if (sis.hasNext("\\d+"))
                {
                    final BigInteger next = sis.nextBigInteger();
                    output("You entered a BigInteger = %s", next);
                }
                else if (sis.hasNextBoolean())
                {
                    final boolean next = sis.nextBoolean();
                    output("You entered a Boolean representation = %s", next);
                }
                else if (sis.hasNext(DATE_PATTERN))
                {
                    final String next = sis.next(DATE_PATTERN);
                    output("You entered a Date representation = %s", next);
                }
                else // unclassified
                {
                    final String next = sis.next();
                    if (isValidURL(next))
                    {
                        output("You entered a valid URL = %s", next);
                    }
                    else
                    {
                        if (EXIT_COMMANDS.contains(next))
                        {
                            output("Exit command %s issued, exiting!", next);
                            break;
                        }
                        else if (HELP_COMMANDS.contains(next)) { output(HELP_MESSAGE); }
                        else { output("You entered an unclassified String = %s", next); }
                    }
                }
            }
            /*
               This will close the underlying InputStream, in this case System.in, and free those resources.
               WARNING: You will not be able to read from System.in anymore after you call .close().
               If you wanted to use System.in for something else, then don't close the Scanner.
            */
            sis.close();
            System.exit(0);
        }
    }
    

    这看起来可能有很多代码,但它说明了正确使用scanner类所需的最小努力,并且不必处理困扰编程新手和这个名为java.util.scanner的实现非常糟糕的类的细微bug和副作用。它试图说明惯用Java代码的外观和行为。

    下面是我写这个例子时思考的一些事情:

    我特意让这个示例与JDK6兼容。如果某个场景真的需要JDK7/8I的特性,或者其他人会发布一个新的答案,说明如何为该版本JDK修改该特性。

    这门课的大部分问题都是学生提出来的,他们通常会限制自己能用什么来解决问题,所以我尽可能地限制了这些问题,以展示如何在没有其他依赖的情况下做一些普通的事情。在22年多的时间里,我一直在使用Java并咨询大部分时间,在我所见过的数百万行源代码中,我从未遇到过这个类的专业使用。

    这确切地显示了如何以惯用的方式从用户交互地读取命令并分派这些命令。关于java.util.scanner的大多数问题是,当我输入某个特定的输入类别时,如何使程序退出。这清楚地表明了这一点。

    我很少看到有人正确地使用.hasnext(),通过测试通用的.hasnext()来控制事件循环,然后使用if(.hasnextxxx())习惯用法,您可以决定如何和如何继续处理代码,而不必担心在没有可用的情况下请求int,从而没有异常处理代码。

    这是一个打破每个人代码的东西。这是一个不应该被处理的细节,并且有一个非常混乱的错误,很难解释,因为它打破了最小震惊的原则

    .nextxxx()方法不使用行尾。.nextline()

    这意味着在.nextxxx()之后立即调用.nextline()将只返回行尾。你必须再次调用它才能真正得到下一行。

    这就是为什么许多人主张要么只使用.nextxxx()方法,要么只使用.nextline()方法,但不能同时使用这两种方法,这样就不会让这种挑剔的行为绊倒您。就我个人而言,我认为类型安全方法比必须手动测试、解析和捕获错误要好得多。

    注意,代码中没有使用可变变量,这对于学习如何做很重要,它消除了运行时错误和细微bug的四个最主要来源。

    >

  • nonulls表示不存在nullPointerExceptions

    没有可变性意味着您不必担心方法参数的更改或其他任何更改。在逐步调试过程中,您不必使用watch来查看哪些变量被更改为哪些值(如果它们正在更改)。这使得当你读到它时,逻辑100%是确定性的。

    没有副作用。如果什么都不能改变,你就不必担心某些边缘情况下的一些微妙的副作用会出人意料地改变一些东西!

    如果您不理解如何在自己的代码中应用final关键字,请阅读以下内容。

    请注意我是如何使用set 和使用.contains()来对命令进行分类的,而不是使用大量的switchif/elseif来进行分类的,这会使您的代码膨胀,更重要的是使维护变成噩梦!添加一个新的重载命令就像在构造函数中的数组中添加一个新的string一样简单。

    这对于i18ni10n以及适当的ResourceBundles也非常有效。map 可以让您以很少的开销获得多种语言支持!

    我已经决定,我的所有代码都应该显式声明某个东西是@nonnull还是@nullable。它允许IDE帮助警告您关于NullPointerException的潜在危险以及您不必检查的时候。

    最重要的是,它记录了对未来读者的期望,即这些方法参数都不应该是null

    在你做这件事之前好好考虑一下。

    如果调用sis.close(),您认为system.in会发生什么?请参阅上面列表中的注释。

  •  类似资料:
    • 问题内容: 我是从记录用户输入使用。我需要验证输入内容,例如: 必须为非负数 它必须是字母 …等等 最好的方法是什么? 问题答案: 示例1:验证正整数 这是一个用于int从输入中验证肯定的简单示例。 结果: 请注意,与更详细组合相比,使用起来要容易得多。通过合同,一个 保证,如果它,然后将安静地给你int,并不会引发任何。 示例2:同一令牌上有多个 请注意,上面的代码段包含一个语句,以使Scann

    • 我有一个耗时的任务,我想在用户输入“abort”时手动中止它。 可运行:

    • 问题内容: 我正在尝试为我的程序创建一个简单的菜单来读取用户输入。这是代码: 这是我得到的例外测试: 我从while循环中删除了第二个命令= Scanner.next(),但是让我仅读取一次用户输入。怎么解决呢? 更新:AddWord方法: 问题答案: 您可以使用

    • 我想从STDIN读取输入。我在C中使用fork()方法。我有子进程和父进程。我的输入是多行的。父进程只等待子进程终止,子进程只读取第一行,子进程终止后,父进程继续读取。我想要打印行。例如;输入-> null 子进程打印“星期一”,父进程打印“星期二”和“星期三”。一旦到达文件结尾,程序就会终止。 ./program

    • 问题内容: 我遇到一种情况(在硒测试期间),在这种情况下,用户将收到安全代码。然后,用户必须先输入安全密码,然后才能继续操作。 我不太确定如何获得用户输入的值。我浏览了硒文档,并提出了这个建议。不幸的是,它并不是很有效。 有人可以指出我正确的方向吗? 问题答案: 似乎您必须先接受并关闭提示,然后才能存储和使用该值

    • 问题内容: 我有一种情况(在selenium测试期间),在这种情况下,用户将收到安全代码。然后,用户必须先输入安全代码,然后才能继续操作。 我不太确定如何获得用户输入的值。我浏览了selenium文档,并提出了这个建议。不幸的是,它并不是很有效。 有人可以指出我正确的方向吗? 问题答案: 似乎您必须先接受并关闭提示,然后才能存储和使用该值