正则表达式:解析多个引用的名称/值对

时间:2018-02-28 20:35:53

标签: php regex pcre

使用PHP 5.6 / 7.0。

我已经在几个正则表达式网站上尝试了几个问题和几个小时的几个正则表达式,并且无法找到任何能够满足我需要的东西。我有一个这样的字符串:

At vero eos et accusamus et iusto odio dignissimos ducimus

<!-- @@include default="/admin/creditapp/templates/longform/" try="/wTemplates/forms/templates/" file="credit_row_1.txt" -->

qui blanditiis praesentium voluptatum deleniti atque corrupti

<!-- @@include default="/admin/creditapp/templates/longform/" try="/wTemplates/forms/templates/" file="credit_row_2.txt" -->

quos dolores et quas excepturi sint

我正在寻找来自令牌的以下比赛:

<!-- @@include ...the whole thing... -->
default
/admin/creditapp/templates/longform
try
/wtemplates/forms/templates
file
credit_row_1.txt

自然地,每次发现整个群体时都会重复。我可以循环文件并完成它,所以一次只有一个实例是好的。我能提出的唯一表达方式就是:

<!-- @@include (?:(try|default|file)=\"(.+?)\"?)(?:\s*)(?:(try|default|file)=\"(.+?)\"?)(?:\s*)(?:(try|default|file)=\"(.+?)\"?)(?:\s*)-->

哪个是巨大的,并且不允许其他可能性,例如,我不知道,&#34;(尝试| foo | bar |默认)&#34;或某事,或遗漏任何&#34;尝试&#34;或&#34;默认,&#34;例如&#34;(FOO |酒吧|文件)&#34;

在模板中

<!-- @@include    -->

是不变的。里面可以是2到 n 名称=值对。我试过了:

(<!-- @@include (?:(try|default|file)=\"(.+?)\" ?){1,3}-->)

但它只返回找到的姓氏=值。我想我已经接近了,但我无法解决这个问题。

1 个答案:

答案 0 :(得分:1)

PCRE无法存储重复捕获组的不同内容。重复组时,先前的内容会被当前内容覆盖,等等 一种解决方法是使用preg_match_all并使用匹配上一个匹配后的下一个位置的\G锚点(默认情况下它也匹配字符串的开头)。

preg_match_all('~(?:\G(?!\A)|<!-- @@include)\s+(try|default|file)="(.*?)"~', $str, $matches);

这种模式的想法是成功使用第一个匹配的第二个分支<!-- @@include,然后使用第一个分支\G(?!\A)进行所有其他连续匹配。当部分\s+(try|default|file)="(.*?)"失败时,连续性被破坏,正则表达式引擎必须找到下一次出现的<!-- @@include才能继续。

如果您想知道第二个分支何时成功,您只需要在第二个分支中放置一个捕获组:

$result = [];

if ( preg_match_all('~(?:\G(?!\A)|<!-- (@)@include)\s+(try|default|file)="(.*?)"~', $str, $matches, PREG_SET_ORDER) ) {
    foreach ($matches as $m) {
        if ( !empty($m[1]) ) { // test which branch succeeds
            if ( isset($temp) )
                $result[] = $temp;
            $temp=[];
        }
        $temp[$m[2]] = $m[3];    
     }
}

if ( isset($temp) )
    $result[] = $temp;

demo

对于更灵活且能够处理未知密钥的内容,您可以使用两个preg_match_all

$result = [];

if ( preg_match_all('~<!-- @@include\s+\K\w+=".*?"(?:\s+\w+=".*?")*~', $str, $matches) ) {
    foreach ($matches[0] as $params) {
        if ( preg_match_all('~(\w+)="(.*?)"~', $params, $keyvals) )
            $result[] = array_combine($keyvals[1], $keyvals[2]);
    }
}

print_r($result);

demo

请注意,对于大字符串,最后一个解决方案可能更有效,因为第一个模式不是以交替开始,而是使用文字字符串(在这种情况下,pcre正则表达式引擎能够优化研究) 。第二种模式只需要处理短字符串,所以它不是问题。