Перейти к основному содержимому

Чтение пользовательского ввода до тех пор, пока не будет выполнено условие

· 5 мин. чтения

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 .

Как только мы получаем «до свидания» от пользователя, мы прерываем бесконечный цикл while . Другими словами, мы прекращаем чтение пользовательского ввода и возвращаем userData для дальнейшей обработки.

Далее вызовем метод readUserInput в основном методе :

public static void main(String[] args) {
List<String> userData = readUserInput();
System.out.printf("User Input Data:\n%s", String.join("\n", userData));
}

Как мы видим в основном методе, после вызова readUserInput мы распечатываем полученные данные пользовательского ввода.

Теперь давайте запустим приложение, чтобы увидеть, работает ли оно должным образом.

Когда приложение запускается, оно ожидает нашего ввода с подсказкой:

Please enter your data below: (send 'bye' to exit)

Итак, давайте отправим какой-нибудь текст и напишем « bye » в конце:

Hello there,
Today is 19. Mar. 2022.
Have a nice day!
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);
}
}

Теперь давайте быстро пройдемся по методу и поймем, как он работает.

В самом начале мы создали массив String inputLines для хранения строк, которые мы хотим использовать в качестве пользовательского ввода. Затем мы инициализировали ожидаемый List , содержащий ожидаемые данные.

Далее начинается сложная часть. После того, как мы сделали резервную копию текущего объекта System.in в переменной stdin , мы переназначили системный стандартный ввод, вызвав метод System.setIn .

В этом случае мы хотим использовать массив inputLines для имитации пользовательского ввода .

Поэтому мы преобразовали массив в InputStream , в данном случае объект ByteArrayInputStream , и переназначили объект InputStream в качестве стандартного ввода системы.

Затем мы можем вызвать целевой метод и проверить, соответствует ли результат ожидаемому.

Наконец, мы не должны забывать восстановить исходный объект stdin в качестве стандартного ввода системы . Поэтому ставим System.setIn(stdin); в блоке finally , чтобы убедиться, что он все равно будет выполнен.

Это пройдет без какого-либо ручного вмешательства, если мы запустим тестовый метод.

5. Вывод

В этой статье мы рассмотрели, как написать метод Java для чтения пользовательского ввода до тех пор, пока не будет выполнено условие.

Две ключевые техники:

  • Использование класса Scanner из стандартного Java API для чтения пользовательского ввода
  • Проверка каждой входной строки в бесконечном цикле; если условие выполнено, разорвать цикл

Кроме того, мы рассмотрели, как написать тестовый метод для автоматической проверки нашего решения.

Как всегда, исходный код, используемый в этом руководстве, доступен на GitHub .