необязательная группа регулярного выражения пропущена

Я пытаюсь захватить необязательную группу внутри необходимой группы. Это мое регулярное выражение:

BEGIN
(?<body>
     (?<A>.*?)               # in my regex just .*?
     (?<B>START.*?STOP)?
     (?<C>.*?)               # in my regex just .*?
)
END

и для ввода:

junk1 BEGIN junk2 START content STOP junk3 END junk4

Я получаю это:

match: 'BEGIN junk2 START content STOP junk3 END' # ok
group 'body':  ' junk2 START content STOP junk3 ' # ok
group 'A': ''                                     # expected: ' junk2 '
group 'B': not found                              # expected: 'START content STOP'
group 'C': ' junk2 START content STOP junk3 '     # expected: ' junk3 '

группы A и C указаны только для справки

Почему группа B не соответствует, даже если есть правильные данные, и я получаю ожидаемый результат, если группа B не является обязательной?


person CzBuCHi    schedule 19.11.2013    source источник
comment
Вы хотите написать STOP вместо END в (?<B>START.*?END)?, верно?   -  person Qtax    schedule 19.11.2013


Ответы (3)


Почему это не работает

У вас должно быть некоторое базовое понимание того, как работает механизм регулярных выражений и возврат работает. В этом случае это происходит примерно так:

Группа (?<A>.*?) будет соответствовать пустой строке с первой попытки (поскольку именно это означает выражение: пустая строка или больше, если требуется).

Тогда мы попадаем в группу B сразу после BEGIN, так как здесь нет START, вся внутренняя группа выйдет из строя, а необязательная группа будет пропущена.

Группа C будет соответствовать пустой строке. Затем мы пытаемся сопоставить END, что не соответствует. Таким образом, механизм регулярных выражений попытается вернуться к последнему квантификатору. В данном случае в группе C. Он будет делать это до тех пор, пока не будет найдено совпадение (или пробовать квантификаторы перед ним, пока не произойдет сбой).

Таким образом, мы получаем группу C, расширяющуюся до END, и тогда все выражение совпадает.

Пример решения

Можно было бы сделать простое решение, если START/STOP не разрешены внутри, за исключением того, что в необязательной группе вы можете использовать выражение вроде:

BEGIN
(?<body>
     (?<A>
          (?: (?!START|STOP) . )*?   # do not match START nor STOP
     )
     (?<B>START.*?STOP)?
     (?<C>
          (?: (?!START|STOP) . )*?   # do not match START nor STOP
     )
)
END
person Qtax    schedule 19.11.2013
comment
Это решение идеально, спасибо. - но что, если START в регулярном выражении также является регулярным выражением - есть ли способ определить регулярное выражение START только один раз? - person CzBuCHi; 19.11.2013
comment
@CzBuCHi, это невозможно сделать в регулярном выражении .NET. Но вы можете не повторять это так часто, если хотите. Например, вы можете использовать свое старое выражение для группы A. - person Qtax; 19.11.2013

В C# сопоставление с шаблоном управляется шаблоном регулярного выражения (подробнее здесь), а не вводимый текст. Итак, что происходит, так это то, что группе A не требуется ничего сопоставлять, поэтому решение откладывается, группе B не требуется ничего сопоставлять, поэтому решение откладывается, группе C не требуется ничего сопоставлять, но достигнут конец регулярного выражения. Группа C сопоставляется с входной строкой, и ей назначается все, что вы ожидаете от группы B. И если вы используете сопоставление с образцом справа налево, все содержимое будет принадлежать группе A.

person MC ND    schedule 19.11.2013

Более ранние конструкции в шаблоне имеют приоритет над более поздними в шаблоне, а ленивый квантификатор (*?) отдает приоритет более коротким совпадениям. Таким образом, лучшим совпадением всегда будет несоответствие группе A, а поскольку START не может соответствовать первой позиции, оно будет пропущено. Наконец, группа C съест остаток строки, так как END не является обязательным.

Используйте это вместо этого:

BEGIN
(?<body>
    (?<A>.*?)
    (?:
        (?<B>START.*?STOP)
        (?<C>.*?)
    )?
)
END

Это заставит группу A съесть столько, сколько сможет, до первого совпадения B, если оно существует.

person Markus Jarderot    schedule 19.11.2013