Как правильно осуществить проверку на наличие определённых символов в строке?
Прошу помочь и оптимизировать мой код.
Пользователь должен ввести определенные значения (дата, время и т.д.). Если ввел не по шаблону, получает повторный запрос.
Требуется осуществить проверку, содержит ли введенная им строка заданные параметры, например, время в формате ЧЧ:ММ
или дату в формате ДД.ММ.ГГ
.
Пример для проверки времени приведён ниже.
Я написал проверку ввода, но:
- Мог чего-то не учесть;
- Наверняка можно эту проверку сделать лучше и более просто.
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 шт):
Во-первых, приведённый код метода 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
.