Как правильно осуществить проверку на наличие определённых символов в строке?

Прошу помочь и оптимизировать мой код.

Пользователь должен ввести определенные значения (дата, время и т.д.). Если ввел не по шаблону, получает повторный запрос.

Требуется осуществить проверку, содержит ли введенная им строка заданные параметры, например, время в формате ЧЧ:ММ или дату в формате ДД.ММ.ГГ.

Пример для проверки времени приведён ниже.

Я написал проверку ввода, но:

  1. Мог чего-то не учесть;
  2. Наверняка можно эту проверку сделать лучше и более просто.
protected void checkSymbbol() {
    String valuez = "1df6:sdf01";
    String value = valuez.replaceAll("[^\\d:]", "");
    if (value.length() == 5) {
        if (value.contains(":")){
            System.out.println("верно");
        } else {
            System.out.println("ошибка");
        }
    } else
        System.out.println("ошибка");
}

В данном примере проверка, что пользователь ввел 16:01.


Ответы (1 шт):

Автор решения: Nowhere Man

Во-первых, приведённый код метода checkSymbbol практически бесполезен, так как в нём проверяется какая-то внутренняя строка, а не входной параметр, и также не возвращается результат проверки, а что-то выводится на печать.

Во-вторых, приведённый код использует более чем странную логику -- сначала он удаляет из входной строки все символы кроме цифр и двоеточия ':', а затем дополнительно проверяет длину и наличие двоеточия в строке независимо от формата. То есть мало того, что входная строка может содержать всяческий мусор, так ещё и результат может быть "верным", даже если в ней не окажется ни одной цифры, например, для строки вида String valuez = "abcd:<hello>[{}]::qwertyu:!~#$%&:"; представленный код также напечатает "верно" -- в переменной value останется 5 двоеточий.

Поэтому, если стоит задача обеспечить ввод пары двузначных символов, разделённых двоеточием, изначально не следует давать возможность ввода всякого мусора, чтобы искать в нём фрагменты чего-то полезного.

Проще и правильнее задать формат ввода и использовать стандартный метод String::matches(String regex).

protected boolean isPairOfTwoDigitNumbers(String input) {
    return input != null && input.matches("\\d\\d:\\d\\d");
}

Тогда при необходимости можно будет подать на вход такого метода предварительно очищенные данные от пользователя:

String cleanInput = userInput.replaceAll("[^\\d:]", "");
if (isPairOfTwoDigitNumbers(cleanInput)) {
    System.out.println("Хорошие данные: " + cleanInput);
} else {
    throw new IllegalArgumentException("Неправильный ввод: " + userInput);
}

Аналогично при проверке формата можно вводить дополнительные ограничения, например если проверяются не любые двузначные числа, а например время в часах/минутах:

protected boolean isValidTimeHHMM(String input) {
    return input != null && input.matches("([01]\\d|2[0-3]):[0-5]\\d");
}

Примечание: При наличии большого числа однотипных проверок с помощью String::matches также может понадобиться небольшая оптимизация, чтобы при каждом вызове не создавался экземпляр регулярного выражения Pattern, так как вызов input.matches(regex) эквивалентен вызову цепочки Pattern.compile(regex).matcher(input).matches().

private static final Pattern HH_MM = Pattern.compile("([01]\\d|2[0-3]):[0-5]\\d");

protected static boolean isValidTimeHHMM(String input) {
    return input != null && HH_MM.matcher(input).matches();
}

Поскольку первоначальный вопрос был всё же о проверке символов, то стоит указать, что можно отдельно проверить наличие запрещённых символов при помощи более быстрого метода Matcher::find, а затем уже проверять формат, тогда не потребуется очистка входной строки:

private static final Pattern TRASH = Pattern.compile("[^\\d:]");

protected boolean notTrash(String input) {
    return !(input == null || TRASH.matcher(input).find());
}

protected static boolean isValidTimeHHMM(String input) {
    return notTrash(input) && HH_MM.matcher(input).matches();
}

Если исходная задача сводится к обработке уже известных типов данных таких как дата / время, проще и правильнее использовать существующий функционал в Date/Time API в виде DateTimeFormatter и соответствующих методов LocalDate::parse, LocalDateTime::parse.

→ Ссылка