1. 概述
当我们编写Java应用程序来接受用户输入时,有两种可能:单行输入和多行输入。
对于单行输入,处理起来相对直接,我们一直读取直到遇到换行符。然而,我们需要采用不同的方式管理多行用户输入。
在这个教程中,我们将讨论如何在Java中处理多行用户输入。
2. 解决问题的思路
在Java中,我们可以使用Scanner类从用户输入中读取数据。因此,从用户输入读取数据对我们来说并不困难。然而,如果我们允许用户输入多行数据,我们就需要知道何时停止接收用户输入。换句话说,我们需要一个事件来知道何时停止从用户输入中读取。
一个常见的做法是检查用户发送的数据。如果数据符合定义的条件,我们就停止读取输入数据。实际上,这个条件可以根据需求进行调整。
解决问题的一个想法是使用无限循环,逐行读取用户输入。在循环中,我们检查用户发送的每一行。一旦满足条件,我们就跳出无限循环:
while (true) {
String line = ... //get one input line
if (matchTheCondition(line)) {
break;
}
... save or use the input data ...
}
接下来,我们将创建一个方法来实现我们的想法。
3. 使用无限循环解决这个问题
为了简化,在这个教程中,一旦我们的应用程序接收到字符串“bye”(不区分大小写),我们就停止读取输入。
因此,按照之前讨论的想法,我们可以创建一个方法来解决问题:
public static List<String> readUserInput() {
List<String> userData = new ArrayList<>();
System.out.println("Please enter your data below: (send 'bye' to exit) ");
Scanner input = new Scanner(System.in);
while (true) {
String line = input.nextLine();
if ("bye".equalsIgnoreCase(line)) {
break;
}
userData.add(line);
}
return userData;
}
如上代码所示,readUserInput
方法从System.in
中读取用户输入,并将数据存储在userData
列表中。
一旦我们从用户那里收到“bye”,我们就跳出无限的while
循环。换句话说,我们停止读取用户输入,并返回userData
进行进一步处理。
接下来,在main
方法中调用readUserInput
方法:
public static void main(String[] args) {
List<String> userData = readUserInput();
System.out.printf("User Input Data:\n%s", String.join("\n", userData));
}
如我们在main
方法中看到的那样,在调用readUserInput
之后,我们打印出收到的用户输入数据。
现在,让我们启动应用程序,看看它是否按预期工作。
当应用程序启动时,它会提示等待我们的输入:
Please enter your data below: (send 'bye' to exit)
所以,让我们发送一些文本,最后发送“bye”:
Hello there,
Today is 19. Mar. 2022.
Have a nice day!
bye
输入“bye"并按下回车后,应用程序输出我们收集的用户输入数据并退出:
User Input Data:
Hello there,
Today is 19. Mar. 2022.
Have a nice day!
正如我们所见,该方法按预期工作。
4. 单元测试解决方案
我们已经解决了问题并进行了手动测试。然而,随着时间的推移,我们可能需要调整方法以适应新的需求。因此,如果我们能自动测试方法就更好了。
为测试readUserInput
方法编写单元测试与常规测试有所不同。这是因为当readUserInput
方法被调用时,应用程序会被阻塞并等待用户输入。
首先,让我们看看测试方法,然后解释它是如何工作的:
@Test
public void givenDataInSystemIn_whenCallingReadUserInputMethod_thenHaveUserInputData() {
String[] inputLines = new String[]{
"The first line.",
"The second line.",
"The last line.",
"bye",
"anything after 'bye' will be ignored"
};
String[] expectedLines = Arrays.copyOf(inputLines, inputLines.length - 2);
List<String> expected = Arrays.stream(expectedLines).collect(Collectors.toList());
InputStream stdin = System.in;
try {
System.setIn(new ByteArrayInputStream(String.join("\n", inputLines).getBytes()));
List<String> actual = UserInputHandler.readUserInput();
assertThat(actual).isEqualTo(expected);
} finally {
System.setIn(stdin);
}
}
现在,让我们快速浏览一下方法,理解它是如何工作的。
一开始,我们创建了一个String
数组inputLines
来存储我们想要用作用户输入的行。然后,我们初始化了一个包含预期数据的expected
列表。
接下来,关键部分来了。在备份当前的System.in
对象到stdin
变量后,我们通过调用System.setIn
方法重置了系统标准输入。
在这种情况下,我们希望使用inputLines
数组模拟用户输入。
因此,我们将数组转换为InputStream
(在这个例子中是ByteArrayInputStream
),并将InputStream
对象重新分配给系统标准输入。
然后,我们可以调用目标方法,测试结果是否符合预期。
最后,我们不应忘记恢复原始的stdin
对象作为系统标准输入。因此,我们在finally
块中放置System.setIn(stdin)
,确保无论如何都会执行。
如果不进行任何手动干预,运行测试方法就会通过。
5. 总结
在这篇文章中,我们探讨了如何编写Java方法,直到满足某个条件才停止读取用户输入。
关键技巧包括:
- 使用Java标准库中的
Scanner
类读取用户输入 - 在无限循环中检查每行输入;如果满足条件,跳出循环
此外,我们还讨论了如何编写测试方法来自动测试我们的解决方案。
如往常一样,本文使用的源代码可以在GitHub上找到。