Захваченная группа в необязательной части регулярного выражения

Я хочу зафиксировать группу в необязательной части строки.

Например:

В строке "firstName: Bill-lastName: Gates" я хочу захватить 2 группы:

  1. Счет
  2. Ворота

Я использую это регулярное выражение:

firstName:(.*)-lastName:(.*)

Но когда часть lastName необязательна, я все равно хочу захватить первую группу (firstName).

Я использовал это регулярное выражение, чтобы сделать часть lastName необязательной (в группе без захвата):

firstName:(.*)(?:-lastName:(.*))?

Используя это обновленное регулярное выражение, получаются следующие группы:

  • когда часть lastName отсутствует, например "firstName: Bill", захваченные группы:

    1. Bill
    2. /пустой строки/

что правильно,

  • когда присутствуют части firstName и lastName: "firstName: Bill-lastName: Gates", группы неверны:

    1. Bill-lastName:Gates
    2. /пустой/

Я думаю, что это связано с жадностью первой группы захвата, но как настроить это регулярное выражение, чтобы оно работало, когда часть lastName является необязательной?


person domenu    schedule 05.02.2015    source источник


Ответы (2)


Вы правы, это про жадность. Найдите разделитель для первой группы совпадений. Итак, если ваше имя «никогда» не содержит тире, сопоставьте только все, кроме тире, с первой группой совпадений.

firstName:([^-]*)(?:-lastName:(.*))?

firstName:([^-]*)(?:-lastName:(.*))?

Визуализация регулярного выражения

Debuggex Demo

Если вы не можете найти такой разделитель, вам потребуется другой подход. Даже если вы попытаетесь сделать первый шаблон "ленивым", механизм Regex всегда предпочтет большее совпадение, а не совпадение с дополнительным необязательным совпадением.

Это связано с тем, что ленивые группы соответствия будут соответствовать первой строке, удовлетворяющей выражению (! Важная формулировка!)

Может быть вариант с поиском вокруг, но вы также можете использовать или -statement без дополнительных совпадений:

firstName:(.*)-lastName:(.*)|firstName:(.*)

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

person dognose    schedule 05.02.2015
comment
Спасибо, отлично работает! +1 для визуализатора Debuggex и пояснения оператора or. - person domenu; 05.02.2015
comment
Почему вы повторяете одну и ту же строку дважды? - person nhahtdh; 05.02.2015

Несмотря на то, что вы уже приняли ответ @dognose, уверяю вас, что есть имена с тире в них (вы не хотите злить Жан-Клода ван Дамма). Я бы посоветовал сделать так:

    firstName:((?:(?!-lastName:).)*)(?:-lastName:(.*))?

Визуализация регулярного выражения

Debuggex Demo

Из визуализации видно, что (?:(?!-lastName:).) говорит «если за текущей позицией не следует '-lastName:', захватите другой символ»

person asontu    schedule 05.02.2015