Регулярное выражение с переменным количеством групп? - PullRequest
28 голосов
/ 16 февраля 2011

Можно ли создать регулярное выражение с переменным числом групп?

После запуска, например, ...

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 имеют эту функцию.

Ответы [ 6 ]

20 голосов
/ 16 февраля 2011

Согласно документации регулярные выражения Java не могут этого сделать:

Захваченный вход, связанный с группа всегда является подпоследовательностью, которая группа самая последняя соответствует. Если группа оценивается во второй раз из-за количественного определения его ранее зафиксированное значение, если оно есть, будет сохранен, если второй оценка не проходит. Соответствие строки «аба» против выражения (а (б)?) +, например, оставляет группу два на «Б». Все захваченные данные отбрасываются в начале каждого матча.

(выделение добавлено)

3 голосов
/ 05 августа 2011

Вы можете использовать split, чтобы получить нужные поля в массив и проходить через них.

http://download.oracle.com/javase/1,5.0/docs/api/java/lang/String.html#split(java.lang.String)

3 голосов
/ 16 февраля 2011
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.

2 голосов
/ 16 февраля 2011

Я не использовал регулярные выражения Java, но для многих языков ответ: Нет.

Кажется, что группы захвата создаются, когда регулярное выражение анализируется, и заполняется, когда оно соответствует строке. Выражение (a)|(b)(c) имеет три группы захвата, только если можно заполнить одну или две из них. (a)* имеет только одну группу, парсер оставляет последнее совпадение в группе после сопоставления.

0 голосов
/ 19 октября 2015

У меня только что была очень похожая проблема, и мне удалось сделать «переменное число групп», но комбинацию цикла 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();
    }

Вы теряете возможность указывать произвольную длину повторения с помощью * или +, потому что упреждающий просмотр должен быть предсказуемой длины.

0 голосов
/ 17 февраля 2011

Я бы подумал, что возвращение назад препятствует такому поведению, и скажу, что влияние /([\S\s])/ в его группирующем накопительном состоянии на что-то вроде Библии.Даже если это можно сделать, результат непостижим, поскольку группы потеряют позиционное значение.Лучше сделать отдельное регулярное выражение для одинакового вида в глобальном смысле и поместить его в массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...