Php 在另一个捕获中捕获可选组

Php 在另一个捕获中捕获可选组,php,regex,regex-group,Php,Regex,Regex Group,给定输入 ${first_name}, ${last_name}, ${create_date:(Y-m-d)}, ${submit_date:(Y-m-d)} 我可以使用\${(.*)}捕获括号中的所有内容。当后缀出现在示例中的日期上时,我想将其作为自己的组捕获。如何使用一个正则表达式实现这一点?您可以试试这个 \$\{([^:, ]*(?::\(([^)]*)\))?)\} ,其中curely brace内的总值(字符串)被捕获到组1,可选值(字符串)被捕获到组2 如果你不想在组1和组2

给定输入

${first_name}, ${last_name}, ${create_date:(Y-m-d)}, ${submit_date:(Y-m-d)}
我可以使用
\${(.*)}
捕获括号中的所有内容。当后缀出现在示例中的日期上时,我想将其作为自己的组捕获。如何使用一个正则表达式实现这一点?

您可以试试这个

\$\{([^:, ]*(?::\(([^)]*)\))?)\}
,其中curely brace内的总值(字符串)被捕获到
组1
,可选值(字符串)被捕获到
组2

如果你不想在组1和组2之间建立包容关系,你也可以试试这个

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

我认为您最初的方法
\${(.*)}
有一个小问题,即您的通配符
*
包含您不想匹配的
}
符号。事实上,一旦您开始捕获,这是您唯一不想匹配的内容,因此我们会更改:

.*      // match anything zero or more times

但是,您希望匹配大括号内的所有内容,除非它具有结构
某些带下划线的单词:(所需文本)
,在这种情况下,您希望匹配
所需文本

我可以通过使用
操作符来解决这个问题,您可以将它放在捕获组之后,将其定义为可选的。
是贪婪的,这意味着它将首先尝试匹配,并且只有当它失败时,才会尝试在没有可选模式的情况下处理匹配

考虑到这一点,用贪婪的思维来重新表述你想要的东西是有帮助的

而不是说:

我想捕捉花括号里面的任何东西 捕获括号内的值(如果存在)

将其更改为:

我想捕获嵌套在括号内的值 在花括号里,但如果不存在的话,我就 不管支架里有什么

将其转换为正则表达式,我得到:

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

首先查找唯一的符号:

${
尝试与嵌套模式匹配:

([a-z_]+:\()?
这会匹配
任何带下划线的小写单词一次或多次(
+
),后跟
:(

现在,我们匹配零个或多个不是结束标记的字母
}

([^\)}]*)
由于贪婪,它会直接使用嵌套模式。如果做不到这一点,匹配将退回到捕获任何不是结束符号的内容

我能想到的一个不可行的情况是:

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

这将使您“看起来像是一个正常的匹配”。

要构建最佳/最快的模式,您需要允许正则表达式引擎以贪婪的方式尽可能多地执行,这意味着使用诸如
*
+
之类的量词,而不带尾随

以下模式需要第一个捕获组(输出列
[1]
),并使第二个捕获组可选(输出列
[2]

建议的模式:
~\$\{([^:}]+)(?::([^}]+)?~
()

代码:()

您可以忽略
[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)',
  ),
)

您是否可以显示您的代码并明确您希望从示例字符串中得到的结果。示例中的
某物
在哪里?
\$\{(.*?)}
将已经捕获到该结果。或者你的意思是分别捕获“键/值”
\$\{(.*?(:(.*?))\}
?是的@NiettheDarkAbsol,这正是我想要的。非常感谢。
~             #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');
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)',
  ),
)