当引号中嵌入其他括号时提取括号中的字符串

Extract string in brackets when there are other brackets embedded in quotes

我想从字符串中提取括号中的部分:

[list items='["one","two"]' ok="no" b="c"]

我正在使用以下 preg_match 调用:

preg_match('~\[([a-zA-Z0-9_]+)[ ]+([a-zA-Z0-9]+=[^\[]+)\]~s', $string,$match)

但是我对引号内出现的括号有疑问。

我有两个文件

theme.html

[list items=""one","[x]tw"'o"" ok="no" b="c""/]
@book
[button text="t'"extB1" name="ok"'" /]
    Asdfz " s wr aw3r '
[button text="t"'extB2" name="no"'" /]

file.php

$string=file_get_contents('theme.html');
for (;;) { 
    if (!preg_match('~\[([a-zA-Z0-9_]+)[ ]+([a-zA-Z0-9]+=[^\[]+)\]~s', $string,$match)) {
        exit;
    }
    $string=str_replace($match[0], '', $string);
    echo "<pre><br>";
    print_r($match);
    echo "<br></pre>";
}

这是输出:

<pre><br>Array
(
    [0] = [button text="textB1" name="ok"]
    [1] = button
    [2] = text="textB1" name="ok"
)
<br></pre>
<pre><br>Array
(
    [0] = [button text="textB2" name="no"]
    [1] = button
    [2] = text="textB2" name="no"
)
<br></pre>

如您所见,输出不包括

[list items='["one","two"]' ok="no" b="c"]

我知道问题是由嵌入的方括号引起的,但我不知道如何更正代码以忽略它们。

您可以使用您的 preg_match 调用的这种变体:

if (!preg_match('~\[(\w+)\s+(\w+=(?:\'[^\']*\'|[^\[])+?)\]~s', $string, $match))

使用 \'[^\']*\' 它会检测引号的存在,并会抓取所有字符直到下一个引号,而不会阻塞左括号。仅当无法匹配时,它才会用于您拥有的部分:[^\[])+。我在其中添加了一个 ?,使其成为非贪婪的,这确保它不会获取结束 ].

还要注意[a-zA-Z_]可以简写成\w,而[ ]可以写成\s,这样也可以让其他的white-space ,我认为没问题。

eval.in 上查看 运行。

备选方案:只匹配完整的行

如果引号可以出现在任何地方而不能保证右括号出现在引号内,那么上面的方法将不起作用。

相反,我们可以要求匹配必须跨越文本中的完整行:

if (!preg_match('~^\s*\[(\w+)\s+(\w+=.*?)\]\s*$~sm', $string, $match))

eval.in 上查看 运行。