Регулярное выражение с переменным числом групп?
Можно ли создать регулярное выражение с переменным числом групп?
После запуска этого примера...
Pattern p = Pattern.compile("ab([cd])*ef");
Matcher m = p.matcher("abcddcef");
m.matches();
... Я хотел бы иметь что-то вроде
-
m.group(1)
= "c"
-
m.group(2)
= "d"
-
m.group(3)
= "d"
-
m.group(4)
= "c"
.
(Предыстория: я разбираю некоторые строки данных, и одно из "полей" повторяется. Я бы хотел избежать цикла matcher.find
для этих полей.)
Как указано @Tim Pietzcker в комментариях, perl6 и . NET имеют эту функцию.
Ответы
Ответ 1
Согласно документации, регулярные выражения Java не могут этого сделать:
Захваченный вход, связанный с группа всегда является подпоследовательностью, которая группа в последний раз. Если группа оценивается второй раз из-за ранее зафиксированное значение, если оно есть, будут сохранены, если вторая оценка не выполняется. Соответствие строки "aba" против выражения (a (b)?) +, например, оставляет группу 2, установленную в "Б". Весь захваченный вход отбрасывается в начале каждого матча.
(выделено курсивом)
Ответ 2
Pattern p = Pattern.compile("ab(?:(c)|(d))*ef");
Matcher m = p.matcher("abcdef");
m.matches();
должен делать то, что вы хотите.
EDIT:
@aioobe, я понимаю сейчас. Вы хотите сделать что-то вроде грамматики
A ::== <Foo> <Bars> <Baz>
Foo ::== "foo"
Baz ::== "baz"
Bars ::== <Bar> <Bars>
| ε
Bar ::== "A"
| "B"
и вытащите все индивидуальные совпадения Bar
.
Нет, нет способа сделать это с помощью java.util.regex
. Вы можете повторять и использовать регулярное выражение в матче Bars
или использовать генератор синтаксического анализатора, например ANTLR, и прикрепить побочный эффект к Bar
.
Ответ 3
Вы можете использовать split, чтобы получить нужные вам поля в массив и пройти через это.
http://download.oracle.com/javase/1,5.0/docs/api/java/lang/String.html#split(java.lang.String)
Ответ 4
Я не использовал java regex, но для многих языков ответ: No.
Захватывающие группы, похоже, создаются, когда регулярное выражение анализируется и заполняется, когда оно соответствует строке. Выражение (a)|(b)(c)
имеет три группы захвата, только если один или два из них могут быть заполнены. (a)*
имеет только одну группу, парсер оставляет последнее совпадение в группе после сопоставления.
Ответ 5
Я бы подумал, что откат тормозит это поведение и говорит о влиянии /([\S\s])/
на накопительное состояние группировки на нечто вроде Библии. Даже если это можно сделать, вывод непознаваем, поскольку группы потеряют позиционное значение. Лучше сделать отдельное регулярное выражение на подобном виде в глобальном смысле и внести его в массив.
Ответ 6
У меня только была очень похожая проблема, и мне удалось сделать "переменное число групп", но комбинация цикла while и сброса совпадения.
int i=0;
String m1=null, m2=null;
while(matcher.find(i) && (m1=matcher.group(1))!=null && (m2=matcher.group(2))!=null)
{
// do work on two found groups
i=matcher.end();
}
Но это для моей проблемы (с двумя повторяющимися
Pattern pattern = Pattern.compile("(?<=^ab[cd]{0,100})[cd](?=[cd]{0,100}ef$)");
Matcher matcher = pattern.matcher("abcddcef")
int i=0;
String res=null;
while(matcher.find(i) && (res=matcher.group())!=null)
{
System.out.println(res);
i=matcher.end();
}
Вы теряете возможность указывать произвольную длину повторения с помощью *
или +
, потому что внешний вид и внешний вид должны быть предсказуемой длины.