如何使用java.util.Scanner正确读取System.in中的用户输入并对其进行操作?

weixin_38116750 2019-09-12 12:56:58
This is meant to be a canonical question/answer that can be used as a duplicate target. These requirements are based on the most common questions posted every day and may be added to as needed. They all require the same basic code structure to get to each of the scenarios and they are generally dependent on one another. 扫描仪看起来像一个“简单”的类,这是第一个错误的地方.它并不简单,它具有各种非明显的副作用和异常行为,以非常微妙的方式打破了最小惊讶原则. 所以这对于这个课程来说似乎有些过分,但洋葱错误和问题的剥离都很简单,但由于它们的相互作用和副作用,它们非常复杂.这就是为什么每天在Stack Overflow上有很多关于它的问题. 常见扫描仪问题: 大多数扫描仪问题包括多次尝试失败. >我希望能够让我的程序在每次上一次输入后自动等待下一个输入.>我想知道如何检测退出命令并在输入该命令时结束我的程序.>我想知道如何以不区分大小写的方式匹配exit命令的多个命令.>我希望能够匹配正则表达式模式以及内置基元.例如,如何匹配似乎是日期(2014/10/18)?>我想知道如何匹配可能无法通过正则表达式匹配实现的内容 – 例如,URL(http://google.com). 动机: 在Java世界中,Scanner是一个特例,它是一个非常挑剔的课程,教师不应该给新学生使用说明.在大多数情况下,教师甚至不知道如何正确使用它.如果在专业的生产规范中使用它几乎没有,所以它对学生的价值是非常值得怀疑的. 使用扫描仪意味着这个问题和答案提到的所有其他事情.扫描仪不仅仅是关于如何解决扫描仪的这些常见问题,这些问题在扫描仪错误的几乎所有问题中始终存在共病问题.它永远不会只是大约next() vs nextLine(),这只是该类实现的挑剔的一个症状,代码发布中总会有其他问题询问有关Scanner的问题. 答案显示了99%的使用Scanner并在StackOverflow上询问的情况的完整,惯用的实现. 特别是在初学者代码中.如果您认为这个答案过于复杂,那么请向讲师告诉新学生使用Scanner,然后再解释复杂性,怪癖,非明显副作用及其行为的特殊性. 关于Principle of least astonishment的重要性以及为什么一致的行为和语义在命名方法和方法参数中很重要,扫描仪是一个很好的教学时刻. 给学生注意: You will probably never actually see Scanner used in professional/commercial line of business apps because everything it does is done better by something else. Real world software has to be more resilient and maintainable than Scanner allows you to write code. Real world software uses standardized file format parsers and documented file formats, not the adhoc input formats that you are given in stand alone assignments.
...全文
234 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
weixin_38117590 2019-09-12
  • 打赏
  • 举报
回复
惯用例: 以下是如何正确使用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); } } 笔记: This may look like a lot of code, but it illustrates the minimum effort needed to use the Scanner class correctly and not have to deal with subtle bugs and side effects that plague those new to programming and this terribly implemented class called java.util.Scanner. It tries to illustrate what idiomatic Java code should look like and behave like. 下面是我写这个例子时我想到的一些事情: JDK版本: 我故意保持这个示例与JDK 6兼容.如果某些场景确实需要JDK 7/8的一个功能我或其他人会发布一个新的答案,详细说明如何为该版本JDK修改它. 关于这门课程的大多数问题都来自学生,他们通常会限制他们可以用来解决问题的方法,因此我尽可能地限制这一点,以显示如何在没有任何其他依赖关系的情况下完成常见的事情. 22年来,我一直在使用Java并在大部分时间内进行咨询,我从未在我看过的数百万行源代码中遇到过这个类的专业用法. 处理命令: 这显示了idiomatically如何以交互方式从用户读取命令并分派这些命令.关于java.util.Scanner的大多数问题是我如何在输入某个特定输入类别时让我的程序退出.这清楚地表明了这一点 天真的调度员 调度逻辑是故意天真的,以免使新读者的解决方案复杂化.基于战略模式或责任链模式的调度员更适合于更复杂的现实世界问题. 错误处理 代码是故意构造的,不需要异常处理,因为没有一些数据可能不正确的情况. .hasNext()和.hasNextXxx() 我很少看到有人正确使用.hasNext(),通过测试通用.hasNext()来控制事件循环,然后使用if(.hasNextXxx())习语让你决定如何以及如何继续你的代码当没有可用时,不必担心要求int,因此没有异常处理代码. .nextXXX()vs .nextLine() 这是破坏每个人代码的东西.这是一个finicky detail,不应该被处理,并有一个非常有说服力的错误,很难说是因为它打破了Principal of Least Astonishment .nextXXX()方法不消耗行结尾. .nextLine()可以. 这意味着在.nextXXX()之后立即调用.nextLine()将返回行结尾.你必须再次调用它来实际获得下一行. 这就是为什么许多人提倡使用.nextXXX()方法或仅使用.nextLine(),但不能同时使用两者,以便这种挑剔的行为不会让你失望.就个人而言,我认为类型安全方法比必须手动测试和解析并捕获错误要好得多. 不可变性: 请注意,代码中没有使用可变变量,这对于学习如何操作很重要,它消除了四个最主要的运行时错误和微妙的错误. > No nulls意味着没有NullPointerExceptions的可能性!>不可变性意味着您不必担心方法参数更改或其他任何更改.当您逐步调试时,您永远不必使用watch来查看哪些变量会更改为哪些值,如果它们正在更改.这使得逻辑在读取时具有100%的确定性.>不可变性意味着您的代码自动是线程安全的.>没有副作用.如果没有什么可以改变的话,你不必担心某些边缘情况的一些微妙的副作用会意外地改变一些东西! Read this if you don’t understand how to apply the final keyword in your own code. 使用Set而不是大量开关或if / elseif块: 请注意我如何使用Set< String>并使用.contains()对命令进行分类,而不是大规模的开关,或者if / elseif monstrosity会使代码膨胀,更重要的是使维护成为一场噩梦!添加新的重载命令就像在构造函数中向数组添加新String一样简单. 这也适用于i18n和i10n以及适当的ResourceBundles.地图< Locale,Set< String>>会让你有很多语言支持,只需很少的开销! @Nonnull 我已经决定我的所有代码都应该在explicitly声明是否有@Nonnull或@Nullable.它允许您的IDE帮助警告您潜在的NullPointerException危险以及何时无需检查. 最重要的是,它记录了未来读者对这些方法参数都不应为空的期望. 调用.close() 在你做之前真的想一想这个. 如果你打电话给sis.close(),你觉得System.in会怎么样?请参阅上面列表中的评论. 请fork and send pull requests,我将更新此问题并回答其他基本使用方案.

435

社区成员

发帖
与我相关
我的任务
社区描述
其他技术讨论专区
其他 技术论坛(原bbs)
社区管理员
  • 其他技术讨论专区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧