Несколько совпадений с разделителем
это мое регулярное выражение:
([+-]*)(\\d+)\\s*([a-zA-Z]+)
- группа №1 = знак
- группа no.2 = множитель
- группа № 3 = единица времени
Дело в том, что я хотел бы сопоставить данный ввод, но он может быть "цепным". Поэтому мой ввод должен быть действительным тогда и только тогда, когда весь шаблон повторяется без каких-либо событий между этими вхождениями (за исключением пробелов). (Только одно совпадение или несколько совпадений рядом друг с другом с возможными пробелами между ними).
действительные примеры:
1day
+1day
-1 day
+1day-1month
+1day +1month
+1day +1month
Недопустимые примеры:
###+1day+1month
+1day###+1month
+1day+1month###
###+1day+1month###
###+1day+1month###
В моем случае я могу использовать метод matcher.find(), это сделало бы трюк, но он примет вход вроде этого: +1day###+1month
, который недействителен для меня.
Любые идеи? Это можно решить с помощью нескольких условий IF и нескольких проверок для начальных и конечных индексов, но я ищу элегантное решение.
ИЗМЕНИТЬ
Предлагаемое регулярное выражение в комментариях ниже ^\s*(([+-]*)(\d+)\s*([a-zA-Z]+)\s*)+$
будет частично выполнять трюк, но если я его использую в коде ниже, он возвращает другой результат, чем результат, который я ищу.
Проблема в том, что я не могу использовать (*my regex*)+
, потому что он будет соответствовать всему.
Решение может состоять в том, чтобы сопоставить весь ввод с ^\s*(([+-]*)(\d+)\s*([a-zA-Z]+)\s*)+$
, а затем использовать ([+-]*)(\\d+)\\s*([a-zA-Z]+)
с matcher.find()
и matcher.group(i)
, чтобы извлечь каждое соответствие и его группы. Но я искал более элегантное решение.
Ответы
Ответ 1
Это должно сработать для вас:
^\s*(([+-]*)(\d+)\s*([a-zA-Z]+)\s*)+$
Во-первых, добавив начальные и конечные привязки (^
и $
), шаблон не допустит появления недопустимых символов где-либо до или после матча.
Далее я включил опциональные пробелы до и после повторного шаблона (\s*
).
Наконец, весь шаблон заключен в ретранслятор, так что он может встречаться несколько раз подряд ((...)+
).
На стороне, заметьте, я также рекомендовал бы изменить [+-]*
на [+-]?
, чтобы он мог произойти только один раз.
Онлайн-демонстрация
Ответ 2
Вы можете использовать ^$
для этого, чтобы соответствовать началу/концу строки
^\s*(?:([+-]?)(\d+)\s*([a-z]+)\s*)+$
https://regex101.com/r/lM7dZ9/2
См. Unit Tests
для ваших примеров. В принципе, вам просто нужно разрешить повторение шаблона и заставить ничего, кроме пробелов, между матчами.
В сочетании с совпадением начала и конца строки, и все готово.
Ответ 3
Вы можете использовать String.matches
или Matcher.matches
в Java для соответствия всей области.
Пример Java:
public class RegTest {
public static final Pattern PATTERN = Pattern.compile(
"(\\s*([+-]?)(\\d+)\\s*([a-zA-Z]+)\\s*)+");
@Test
public void testDays() throws Exception {
assertTrue(valid("1 day"));
assertTrue(valid("-1 day"));
assertTrue(valid("+1day-1month"));
assertTrue(valid("+1day -1month"));
assertTrue(valid(" +1day +1month "));
assertFalse(valid("+1day###+1month"));
assertFalse(valid(""));
assertFalse(valid("++1day-1month"));
}
private static boolean valid(String s) {
return PATTERN.matcher(s).matches();
}
}
Ответ 4
Вы можете выполнить следующее:
String p = "\\G\\s*(?:([-+]?)(\\d+)\\s*([a-z]+)|\\z)";
Pattern RegexCompile = Pattern.compile(p, Pattern.CASE_INSENSITIVE);
String s = "+1day 1month";
ArrayList<HashMap<String, String>> results = new ArrayList<HashMap<String, String>>();
Matcher m = RegexCompile.matcher(s);
boolean validFormat = false;
while( m.find() ) {
if (m.group(1) == null) {
// if the capture group 1 (or 2 or 3) is null, it means that the second
// branch of the pattern has succeeded (the \z branch) and that the end
// of the string has been reached.
validFormat = true;
} else {
// otherwise, this is not the end of the string and the match result is
// "temporary" stored in the ArrayList 'results'
HashMap<String, String> result = new HashMap<String, String>();
result.put("sign", m.group(1));
result.put("multiplier", m.group(2));
result.put("time_unit", m.group(3));
results.add(result);
}
}
if (validFormat) {
for (HashMap item : results) {
System.out.println("sign: " + item.get("sign")
+ "\nmultiplier: " + item.get("multiplier")
+ "\ntime_unit: " + item.get("time_unit") + "\n");
}
} else {
results.clear();
System.out.println("Invalid Format");
}
Якорь \G
соответствует началу строки или позиции после предыдущего совпадения. В этом шаблоне он гарантирует, что все совпадения совпадают. Если конец строки достигнут, это доказательство того, что строка действительна от начала до конца.