#java #regex
#java #регулярное выражение
Вопрос:
Используя Java 7 и реализацию регулярных выражений по умолчанию в java.util.regex.Pattern, учитывая регулярное выражение, подобное этому:
^start (m[aei]ddel[0-9] ?) tail$
И строка, подобная этой:
start maddel1 meddel2 middel3 tail
Возможно ли получить такой результат, используя привязанное регулярное выражение:
start <match> <match> <match> tail
.
Я могу получить каждую группу без таких якорей:
Регулярное выражение: m[aei]ddel[0-9]
StringBuffer sb = new StringBuffer();
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
matcher.appendReplacement(sb, Matcher.quoteReplacement("<middle>"));
}
Проблема в том, что я работаю над довольно большим набором данных, и возможность привязки шаблонов привела бы к огромному выигрышу в производительности.
Однако, когда я добавляю якоря, единственный API, который я могу найти, требует полного совпадения и доступа к последнему вхождению группы. В моем случае мне нужно убедиться, что регулярное выражение действительно совпадает (т. Е. Полностью совпадает), но на этапе замены мне нужно иметь возможность доступа к каждой группе самостоятельно.
редактировать Я бы хотел избежать обходных путей, таких как поиск якорей на отдельном шаге, потому что это потребовало бы больших изменений в коде, и все это в регулярных выражениях выглядит более элегантно.
Комментарии:
1. Вы просто заменяете каждую
maddle
на<middle>
?2. да, я не думаю, что мне нужно выполнять больше задач для согласованной последовательности
3. Чтобы уточнить:
m[aei]ddel[0-9]
это всего лишь пример, мои фактические регулярные выражения более сложные, и каждое регулярное выражение будет иметь другую замену. Важным моментом является то, что мне нужно заменять каждое вхождение группы соответствия на ее собственную замену, и регулярное выражение должно быть привязано.4. Вы можете сделать это в два этапа: сопоставьте привязанное предложение, затем замените сгруппированные слова в этом предложении.
5. Да, подумал об этом, просто любопытно, есть ли API для выполнения этого без разделения двух шагов (допускал бы локальные изменения, тогда как разделение означает большее изменение всей системы вплоть до базы данных, где хранятся регулярные выражения)
Ответ №1:
Вы можете использовать G
для этого:
final String regex = "(^start |(?<!^)\G)m[aei]ddel[0-9] (?=.* tail$)";
final String str = "start maddel1 meddel2 middel3 tail";
String repl = str.replaceAll(regex, "$1<match> ");
//=> start <match> <match> <match> tail
Демонстрация регулярных выражений
G
утверждает позицию в конце предыдущего совпадения или в начале строки для первого совпадения.
Комментарии:
1. Это регулярное выражение также найдет совпадения в строке типа
start maddel1 meddel2 middel3 something else tail
Ответ №2:
Чтобы сделать это за один шаг, вам нужно использовать G
регулярное выражение на основе, которое будет выполнять привязку. Однако вам также нужен положительный прогноз, чтобы проверить, заканчивается ли строка желаемым шаблоном.
Вот регулярное выражение, которое должно работать:
(^start|(?!A)G)s m[aei]ddel[0-9](?=(?:s m[aei]ddel[0-9])*s tail$)
Смотрите демонстрацию регулярных выражений
String s = "start maddel1 meddel2 middel3 tail";
String pat = "(^start|(?!\A)\G)\s (m[aei]ddel[0-9])(?=(?:\s m[aei]ddel[0-9])*\s tail$)";
System.out.println(s.replaceAll(pat, "$1 <middle>" ));
Смотрите онлайн-демонстрацию Java
Объяснение:
(^start|(?!A)G)
— совпадениеstart
в конце строки или в конце предыдущего успешного совпаденияs
— 1 или более пробеловm[aei]ddel[0-9]
—m
, затем либоa
,e
,i
, затемddel
, затем 1 цифра(?=(?:s m[aei]ddel[0-9])*s tail$)
— только если за ним следует:(?:s m[aei]ddel[0-9])*
— ноль или более последовательностей из 1 пробелов иmiddelN
шаблонаs
— 1 или более пробеловtail$
—tails
подстрока, за которой следует конец строки.
Комментарии:
1. Хороший вызов, что регулярное выражение anubhava также будет соответствовать другим входным данным. Жаль, что все становится намного сложнее (с точки зрения синтаксиса и требует обходных путей). Я попробую и надеюсь, что мои товарищи по команде смогут жить с немного более пугающими регулярными выражениями 😉
2. Если вы не хотите жестко кодировать пробелы, переместите шаблон пробелов в группу 1, см. ideone.com/5nPrri . Вы все равно можете использовать двухэтапный подход, сначала заменив строку на
"^start .* tail\z"
, а затем заменив то, что вам нужно.
Ответ №3:
С G
помощью привязки для find
метода вы можете записать его следующим образом:
pat = "\G(?:(?!\A) |\Astart (?=(?:m[aei]ddel[0-9] ) tail\z))(m\S )";
Подробные сведения:
\G # position after the previous match or at the start of the string
# putting it in factor makes fail the pattern more quickly after the last match
(?:
(?!\A) [ ] # a space not at the start of the string
# this branch is the first one because it has more chance to succeed
|
\A start [ ] # "start " at the beginning of the string
(?=(?:m[aei]ddel[0-9] ) tail\z) # check the string format once and for all
# since this branch will succeed only once
)
( # capture group 1
m\S # the shortest and simplest pattern that matches "m[aei]ddel[0-9]"
# and excludes "tail" (adapt it to your need but keep the same idea)
)
Комментарии:
1. Честно говоря, мне нравятся ваши решения.