代码之家  ›  专栏  ›  技术社区  ›  Bill in Kansas City

regex:解析多个带引号的名称/值对

  •  0
  • Bill in Kansas City  · 技术社区  · 6 年前

    使用PHP 5.6/7.0。

    我在几个regex网站上尝试了几个问题和几个小时的regex,但找不到任何能满足我需要的东西。我有这样一个字符串:

    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*)-->
    

    这是巨大的,并且不允许其他可能性,例如,我不知道,“(try | foo | bar | default)”或其他什么,或者省略“try”或“default”,例如“(foo | bar | file)。”

    在模板中

    <!-- @@include    -->
    

    是常量。里面可以是2到 n 名称=值对。我试过:

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

    但它只返回last name=找到的值。我想我已经很接近了,但我无法解决。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Casimir et Hippolyte    6 年前

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

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

    这种模式的想法是在第二个分支上取得成功 <!-- @@include 对于第一个匹配,然后与第一个分支 \G(?!\A) 对于所有其他连续比赛。当零件 \s+(try|default|file)="(.*?)" 失败,连续性被破坏,正则表达式引擎必须找到下一个 <!-- @@包括 继续。

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

    $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\u match\u全部 :

    $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 regex引擎能够优化研究)。第二种模式只需要处理短字符串,所以这不是问题。