如何使用java.util.Scanner正确读取System.in中的用户input并对其执行操作?
这是一个典型的问题/答案 ,可以用作重复的目标。 这些要求基于每天发布的最常见的问题,并可根据需要添加。 它们都需要相同的基本代码结构来获取每个场景,并且它们通常相互依赖。
如何正确读取用户提供的System.in
各种types的input并对其执行操作?
-
我希望能够让我的程序在每个之前的input之后自动等待下一个input。
-
我不想使用JDK中没有的任何东西。
-
我想知道如何检测退出命令,并input命令时结束我的程序。
-
我想知道如何以不区分大小写的方式匹配exit命令的多个命令。
-
我希望能够匹配正则expression式模式以及内置的基元。 例如,如何匹配似乎是一个date(
2014/10/18
)? -
我想知道如何匹配可能不易用正则expression式匹配实现的内容,例如URL(
http://google.com
)。
习惯例子:
以下是如何正确使用java.util.Scanner
类来正确交互地从System.in
读取用户input(有时称为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 Readable, in this case System.in, and free those resources. WARNING: You will not be able to read from System.in anymore after this you call .close(). If you wanted to use System.in for something else, then don't close the Scanner. */ sis.close(); System.exit(0); } }
笔记:
JDK版本:
我故意保持这个例子与JDK 6兼容。如果某些场景确实需要JDK 7/8的特性,我或其他人会发布一个新的答案,具体说明如何修改该版本的JDK。
处理命令:
这正好显示了如何从用户交互式地读取命令并发送这些命令。 关于java.util.Scanner
的大多数问题都是我input某个特定input类别时如何让我的程序退出 。 这表明,更多。
天真的调度员
调度逻辑是故意天真的,不会使新读者的解决scheme复杂化。 基于Strategy Pattern
或Chain Of Responsibility
Strategy Pattern
调度员将更加适合现实世界的问题,这将会更加复杂。
error handling
代码是故意结构化的,不需要Exception
处理,因为没有任何情况下一些数据可能不正确。
.hasNext()和.hasNextXxx()
通过testing通用.hasNext()
来控制事件循环,然后使用if(.hasNextXxx())
成语,您可以决定如何以及如何处理代码而不必担心在没有可用的情况下要求int
,因此没有exception处理代码。
不可变性:
注意,在代码中没有使用可变variables,这对于学习如何去做是非常重要的,它消除了运行时错误和微妙错误的四个最主要的来源。
-
没有
nulls
意味着没有NullPointerExceptions
可能性! -
没有可变性意味着你不必担心方法参数改变或其他任何改变。 当你通过debugging来进行debugging的时候,你无需使用
watch
来查看哪些variables正在改变什么值。 当你读它时,这使得逻辑100%确定。 -
没有可变性意味着你的代码是自动线程安全的。
-
无副作用。 如果没有什么可以改变,你不必担心一些边缘情况一些意想不到的副作用!
如果您不知道如何在自己的代码中应用final
关键字,请阅读本文。
使用Set而不是大量的switch
或if/elseif
块:
请注意,我如何使用Set<String>
并使用.contains()
将命令分类,而不是使用大量的switch
否则会使代码膨胀,更重要的是使维护成为噩梦! 添加一个新的重载命令就像在构造函数中向数组添加一个新的String
一样简单。
这也适用于i18n
和i10n
以及合适的ResourceBundles
。 一个Map<Locale,Set<String>>
可以让你拥有多种语言支持,开销很小!
@Nonnull
我已经决定,我所有的代码应该显式声明,如果是@Nonnull
或@Nullable
。 它使您的IDE可以帮助您警告潜在的NullPointerException
危害,以及何时不必检查。
最重要的是它logging了未来读者的期望,这些方法参数都不应该为null
。
调用.close()
在做之前真的想想这个。
如果你打电话给sis.close()
你sis.close()
呢? 请参阅上面列表中的注释。