1. Обзор
В этом руководстве мы увидим, как использовать метод useDelimiter класса
Scanner
.
2. Введение в java.util.Scanner
Scanner
API предоставляет простой текстовый сканер.
По умолчанию Scanner
разбивает входные данные на токены, используя пробелы в качестве разделителей. Напишем функцию, которая будет:
- передать ввод
сканеру
- повторите через
сканер
, чтобы собрать токены в списке
Давайте посмотрим на базовую реализацию:
public static List<String> baseScanner(String input) {
try (Scanner scan = new Scanner(input)) {
List<String> result = new ArrayList<String>();
scan.forEachRemaining(result::add);
return result;
}
}
Обратите внимание, что в этом фрагменте кода мы использовали попытку с ресурсами
для создания нашего сканера
. Это возможно, потому что класс Scanner реализует интерфейс
AutoCloseable
. Этот блок отвечает за автоматическое закрытие ресурса Scanner. До Java 7 мы не могли использовать try-with-resources
, и поэтому нам приходилось обрабатывать это вручную.
Мы также можем заметить, что для повторения элементов Scanner
мы использовали метод forEachRemaining
. Этот метод был представлен в Java 8. Scanner реализует Iterator
, и нам пришлось бы воспользоваться этим для перебора элементов, если бы мы использовали более старую версию Java.
Как мы уже говорили, Scanner
по умолчанию будет использовать пробелы для анализа ввода. Например, вызов нашего метода baseScanner
со следующим вводом: «Добро пожаловать в ForEach» должен вернуть список, содержащий следующие упорядоченные элементы: «Добро пожаловать», «в», «ForEach».
Давайте напишем тест, чтобы проверить, что наш метод ведет себя так, как ожидалось:
@Test
void whenBaseScanner_ThenWhitespacesAreUsedAsDelimiters() {
assertEquals(List.of("Welcome", "to", "ForEach"), baseScanner("Welcome to ForEach"));
}
3. Используйте настраиваемые разделители
Теперь давайте настроим наш сканер на использование пользовательского разделителя. Мы передадим строку
, которая будет использоваться сканером
для прерывания ввода.
Давайте посмотрим, как мы можем это сделать:
public static List<String> scannerWithDelimiter(String input, String delimiter) {
try (Scanner scan = new Scanner(input)) {
scan.useDelimiter(delimiter);
List<String> result = new ArrayList<String>();
scan.forEachRemaining(result::add);
return result;
}
}
Прокомментируем пару примеров:
- мы можем использовать одиночный символ в качестве разделителя: при необходимости этот символ должен быть экранирован . Например, если мы хотим имитировать базовое поведение и использовать пробелы в качестве разделителей, мы будем использовать «\s». ``
- мы можем использовать любое слово/фразу в качестве разделителя
- мы можем использовать несколько возможных символов в качестве разделителей: для этого мы должны разделить их знаком |. Например, если мы хотим разделить ввод между каждым пробелом и каждым разрывом строки, мы будем использовать следующий разделитель: «\n|\s»
- в двух словах, мы можем использовать любое регулярное выражение в качестве разделителя: например, «a+» является допустимым разделителем
Давайте посмотрим, как мы будем тестировать первый случай:
@Test
void givenSimpleCharacterDelimiter_whenScannerWithDelimiter_ThenInputIsCorrectlyParsed() {
assertEquals(List.of("Welcome", "to", "ForEach"), scannerWithDelimiter("Welcome to ForEach", "\\s"));
}
На самом деле, в сцене метод useDelimiter
преобразует свои входные данные в регулярное выражение , инкапсулированное в объекте Pattern .
В качестве альтернативы мы могли бы сами позаботиться о создании экземпляра Pattern
. Для этого нам нужно будет использовать переопределяющий useDelimiter(Pattern pattern)
, как показано здесь:
public static List<String> scannerWithDelimiterUsingPattern(String input, Pattern delimiter) {
try (Scanner scan = new Scanner(input)) {
scan.useDelimiter(delimiter);
List<String> result = new ArrayList<String>();
scan.forEachRemaining(result::add);
return result;
}
}
Чтобы создать экземпляр Pattern
, мы можем использовать метод compile
, как в следующем тесте:
@Test
void givenStringDelimiter_whenScannerWithDelimiterUsingPattern_ThenInputIsCorrectlyParsed() {
assertEquals(List.of("Welcome", "to", "ForEach"), DelimiterDemo.scannerWithDelimiterUsingPattern("Welcome to ForEach", Pattern.compile("\\s")));
}
4. Вывод
В этой статье мы продемонстрировали несколько примеров шаблонов, которые можно использовать для вызова функции useDelimiter
. Мы заметили, что по умолчанию Scanner
использует разделители-пробелы, и указали, что там можно использовать любые регулярные выражения.
Как всегда, полный код доступен на GitHub .