Захватить необязательную группу в другой записи - PullRequest
0 голосов
/ 27 апреля 2018

С учетом ввода типа

${first_name}, ${last_name}, ${create_date:(Y-m-d)}, ${submit_date:(Y-m-d)}

Я могу использовать \${(.*)} для захвата всего, что находится в скобках. Когда суффикс появляется как на датах в примере, я хочу зафиксировать его как свою собственную группу. Как я могу сделать это с одним регулярным выражением?

Ответы [ 3 ]

0 голосов
/ 27 апреля 2018

Я думаю, что ваш оригинальный подход \${(.*)} имеет небольшую проблему, заключающуюся в том, что ваш подстановочный сопоставитель .* содержит символ }, который вы не хотите сопоставлять. Фактически, как только вы начинаете захват, это единственное, что вы не хотите сопоставлять, поэтому мы меняем:

.*      // match anything zero or more times

до

[^}]*   // match anything that isn't } zero or more times

Но вы хотите сопоставить все в фигурных скобках, если у него нет структуры some_underscored_words:(desired-text), в этом случае вы хотите сопоставить desired-text.

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

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

Вместо того чтобы сказать:

Я хочу захватить все, что находится внутри фигурных скобок, а также захватить значение в скобках, если они существуют

Измените его на:

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

Превращая это в регулярное выражение, я получаю это:

\${([a-z_]+:\()?([^\)}]*)

Разбивка:

Начните с поиска уникального символа:

${

Попробуйте сопоставить с вложенным шаблоном:

([a-z_]+:\()?

Это соответствует any_underscored_lowercase_words один или несколько раз (+), за которыми следует :(

Теперь мы сопоставляем ноль или более букв, которые не являются ни нашими закрывающими тегами ) или }

([^\)}]*)

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

Один сценарий, который я могу придумать, где это не сработает, это:

${seems like a normal match) wait where did these letters go?}

Что даст вам "похоже на нормальное совпадение".

0 голосов
/ 28 апреля 2018

Чтобы построить лучший / самый быстрый шаблон, вам нужно, чтобы механизм регулярных выражений работал как можно более жадным образом, что означает использование квантификаторов, таких как * и + без трейлинга ?.

Следующий шаблон потребует первую группу захвата (выходной столбец [1]) и сделает вторую группу захвата необязательной (выходной столбец [2]).

Предлагаемый паттерн: ~\$\{([^:}]+)(?::([^}]+))?~ ( Демонстрационный паттерн )

~             #starting pattern delimiter
\$\{          #match a dollar sign then opening curly bracket
(             #start Capture Group #1
  [^:}]+      #match one or more non-colon, non-closing curly bracket characters
)             #end Capture Group #1
(?:           #start non-capturing group
  :           #match a colon
  (           #start Capture Group #2
    [^}]+     #match one or more non-closing curly brackets
  )           #end Capture Group #2
)?            #end non-capturing group and allow zero or one occurence of the group
~             #end pattern delimiter

Код: ( Демо )

$string = '${first_name}, ${last_name}, ${create_date:(Y-m-d)}, ${submit_date:(Y-m-d)}';
var_export(preg_match_all('~\$\{([^:}]+)(?::([^}]+))?~', $string, $out, PREG_SET_ORDER) ? $out : 'fail');

Вы можете не учитывать столбец совпадений [0] (fullstring). Вас интересуют только первая и вторая группы захвата.

Выход:

array (
  0 => 
  array (
    0 => '${first_name',
    1 => 'first_name',
  ),
  1 => 
  array (
    0 => '${last_name',
    1 => 'last_name',
  ),
  2 => 
  array (
    0 => '${create_date:(Y-m-d)',
    1 => 'create_date',
    2 => '(Y-m-d)',
  ),
  3 => 
  array (
    0 => '${submit_date:(Y-m-d)',
    1 => 'submit_date',
    2 => '(Y-m-d)',
  ),
)
0 голосов
/ 27 апреля 2018

Вы можете попробовать это,

\$\{([^:, ]*(?::\(([^)]*)\))?)\}

Демонстрация,,, в которой общее значение (строка) внутри фигурной скобки фиксируется в group 1, а необязательное значение (строка) фиксируется в group 2.

и вы можете попробовать это тоже, если не хотите инклюзивного отношения между группами 1 и 2,

\$\{([^:, ]*)(?::\(([^)]*)\))?\}

Демо

...