是否可以将 preg_match 中的所有属性与空属性或缺失属性匹配?

Is it possible to match all attributes in a preg_match with empty or missing attributes?

我在使用 pre_match 时遇到了一些问题。

我有一个字符串可以以任何顺序包含属性(例如 [foobar a="b" c="d" f="g"][foobar c="d" a="b" f="g"][foobar f="g" a="b" c="d"] 等)

这些是我尝试过的模式:

// Matches when all searched for attributes are present
// doesn't match if one of them is missing
// http://www.phpliveregex.com/p/dHi
$pattern = '\[foobar\b(?=\s)(?=(?:(?!\]).)*\s\ba=(["|'])((?:(?!).)*))(?=(?:(?!\]).)*\s\bc=(["'])((?:(?!).)*))(?:(?!\]).)*]'

// Matches only when attributes are in the right order
// http://www.phpliveregex.com/p/dHj
$pattern = '\[foobar\s+a=["\'](?<a>[^"\']*)["\']\s+c=["\'](?<c>[^"\']*).*?\]'

我正在努力弄清楚,但似乎不太对劲。

有没有办法匹配所有属性,即使其他属性缺失或为空 (a='')?

我什至在属性之间的空格处玩过 explode 然后 str_replace,但这似乎太过分了而且不是正确的方法。

在链接中,我只匹配了 a="b"c="d",但我也想匹配这些情况,即使存在 e="f"z="x"

您可以使用以下功能:

function toAssociativeArray($str) {
    // Single key/pair extraction pattern:
    $pattern = '(\w+)\s*=\s*"([^"]*)"';
    $res = array();
    // Valid string?
    if (preg_match("/\[foobar((\s+$pattern)*)\]/", $str, $matches)) {
        // Yes, extract key/value pairs: 
        preg_match_all("/$pattern/", $matches[1], $matches);
        for ($i = 0; $i < count($matches[1]); $i += 1) {
            $res[$matches[1][$i]] = $matches[2][$i];
        }
    };
    return $res;
}

您可以这样使用它:

// Some test data:
$testData = array('[foobar a="b" c="d" f="g"]',
             '[foobar a="b" f="g" a="d"]',
             '[foobar f="g" a="b" c="d"]',
             '[foobar f="g" a="b"]',
             '[foobar f="g" c="d" f="x"]');
// Properties I am interested in, with a default value:
$base = array("a" => "null", "c" => "nothing", "f" => "");
// Loop through the test data:
foreach ($testData as $str) {
    // get the key/value pairs and merge with defaults:
    $res = array_merge($base, toAssociativeArray($str));
    // print value of the "a" property
    echo "value of a is {$res['a']} <br>";
}

此脚本输出:

value of a is b
value of a is d
value of a is b
value of a is b
value of a is null 

如果您将 [...] 字符串作为单独的字符串,而不是在较大的文本中,则很容易使用基于 \G 的正则表达式来标记起始边界 ([some_text) 和然后使用否定字符 类.

将任何键值对与一些基本的正则表达式子模式匹配

这里是the regex:

(?:\[foobar\b|(?!^)\G)\s+\K(?<key>[^=]+)="(?<val>[^"]*)"(?=\s+[^=]+="|])

这是它与人类语言匹配的内容:

  • (?:\[foobar\b|(?!^)\G) - 前导边界,正则表达式引擎在继续之前应该首先找到它,并且它匹配文字 [foobar 或上一个成功匹配的结尾(\G 匹配上次成功匹配之后的字符串开始或位置,因为我们只需要后者,负先行 (?!^) 不包括字符串的开头)
  • \s+ - 1 个或多个空格(它们是用属性值分隔标签名称所必需的)
  • \K - 强制正则表达式引擎忽略目前抓取的所有匹配字符的正则表达式运算符。 PCRE 中正面回顾的一个很酷的替代品。
  • (?<key>[^=]+) - 命名捕获组 "key" 匹配除 =.
  • 以外的 1 个或多个字符
  • =" - 匹配文字 =" 序列 -(?<val>[^"]*) - 命名捕获组 "val" 匹配除 "
  • 之外的 0 个或多个字符(由于 * 量词)
  • " - 文字 " 是值子字符串的结束定界符。
  • (?=\s+[^=]+="|]) - 确保存在下一个属性或 [tag xx="yy"...] 实体的末尾的正向前瞻。

PHP code:

$re = '/(?:\[foobar\b|(?!^)\G)\s+\K(?<key>[^=]+)="(?<val>[^"]*)"(?=\s+[^=]+="|])/'; 
$str = "[foobar a=\"b\" c=\"d\" f=\"g\"]"; 
preg_match_all($re, $str, $matches);
print_r(array_combine($matches["key"], $matches["val"]));

输出:[a] => b, [c] => d, [f] => g.