读取用户输入直到满足条件

2025/04/06

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 List中。

一旦我们收到用户的“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”并按下Enter后,应用程序输出我们收集到的用户输入数据并退出:

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);
    }
}

现在,让我们快速浏览一下该方法并了解其工作原理。

一开始,我们创建了一个字符串数组inputLines来保存我们想要用作用户输入的行。然后,我们初始化了预期的List,其中包含预期的数据。

接下来,棘手的部分来了,在我们将当前System.in对象备份到stdin变量中之后,我们通过调用System.setIn方法重新分配了系统标准输入。

在这种情况下,我们要使用inputLines数组来模拟用户输入

因此,我们将数组转换为InputStream(在本例中为ByteArrayInputStream对象),并将InputStream对象重新分配为系统标准输入。

然后,我们可以调用目标方法并测试结果是否符合预期。

最后,我们不应该忘记恢复原来的stdin对象作为系统标准输入。因此,我们将System.setIn(stdin);放在finally块中,以确保它无论如何都会被执行。

如果我们运行测试方法,它就会通过,无需任何人工干预。

5. 总结

在本文中,我们探讨了如何编写Java方法来读取用户输入,直到满足条件为止。

两个关键技术是:

  • 使用标准Java API中的Scanner类读取用户输入
  • 在无限循环中检查每一行输入;如果条件满足,则中断循环

此外,我们还讨论了如何编写测试方法来自动测试我们的解决方案。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章