Php 使用正则表达式解析专有标记语法-如何检测嵌套标记?

Php 使用正则表达式解析专有标记语法-如何检测嵌套标记?,php,regex,Php,Regex,我想分析如下内容: Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag [{tag2: more data here}] kj udf}] $tree = json_decode('["root","'. preg_replace('/\[\{(\w+):/', '",["\1","',

我想分析如下内容:

Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag
[{tag2: more data here}] kj udf}]
$tree = json_decode('["root","'.
                    preg_replace('/\[\{(\w+):/',
                                 '",["\1","',
                                 str_replace(array('\\',   "\n", '"',  '}]'),
                                             array('\\\\', '\n', '\"', '"],"'),
                                             $str).
                    '"]'));

我使用这个PCRE正则表达式来获取
\[{(.*?}\]
之间的所有数据,它可以工作,但不能用于嵌套标记。我不是PCRE专家。

当使用正则表达式允许无限嵌套时,没有通用的解决方案。它们不是为此而生的

以下内容与由[{和}]分隔的注释相匹配,允许内部有一级嵌套注释。 是否使用负前瞻而不是。*?如果主题字符串包含不平衡的[{个字符,则防止灾难性回溯

\[{(?:(?!}]|\[{).)*+(?:\[{(?:(?!}]|\[{).)*+}](?:(?!}]|\[{).)*+)*+.*?}]

这是正则表达式中的一个常见问题。正如buckley所说,它们不是为此而设计的。尽管如此,这个问题还是出现了很多

基本问题是正则表达式无法知道嵌套标记的右括号实际上不是外部标记的右括号

我决定做一些破坏,并想出了这个恐怖的概念。这个概念是首先拉出不包含其他标签的标签。然后它向外工作,直到它拥有所有标签

$str = "Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag [{tag2: more data here}] kj udf}]";
$matches = array();
function replace_cb($this_match) {
    global $matches;
    $this_match = $this_match[0];
    foreach($matches as $index => $match) $this_match = str_replace('**'.($index + 1).'**', $match, $this_match);
    array_push($matches, $this_match);
    return '**'.count($matches).'**';
}
while(preg_match('/\[\{[^\[]*?\}\]/', $str)) $str = preg_replace_callback('/\[\{[^\[]*?\}\]/', 'replace_cb', $str);
print_r($matches);
产出:

Array
(
    [0] => [{tagname:content}]
    [1] => [{tag2: more data here}]
    [2] => [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag [{tag2: more data here}] kj udf}]
)
…因此,您将得到所有三个标记,它们是分开的

一个缺点是,如果一个标记包含
[
,它当前就决定它包含嵌套的标记。这应该是
[{
,但这很困难,因为您不能对正则表达式中的子字符串求反,只能对字符或字符范围求反

\[{(?:(?!}]|\[{).)*+(?:\[{(?:(?!}]|\[{).)*+}](?:(?!}]|\[{).)*+)*+.*?}]

所以,太可怕了。但它是有效的:)

正则表达式不是解析器。

对于轻量级解决方案,我建议您使用JSON解析器,例如:

Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag
[{tag2: more data here}] kj udf}]
$tree = json_decode('["root","'.
                    preg_replace('/\[\{(\w+):/',
                                 '",["\1","',
                                 str_replace(array('\\',   "\n", '"',  '}]'),
                                             array('\\\\', '\n', '\"', '"],"'),
                                             $str).
                    '"]'));
对于此输入(您的示例):

您将获得以下输出:

$tree = array(
   0 => "root",
   1 => "Hi ",
   2 => array(
      0 => "tagname",
      1 => "content"
   ),
   3 => " ",
   4 => array(
      0 => "tag1",
      1 => "xnkudfdhkfujhkdjki diidfo now nested tag\n",
      2 => array(
         0 => "tag2",
         1 => " more data here"
      ),
      3 => " kj udf"
   ),
   5 => ""
);
标记名是每个子树的元素
0
(我添加了一个任意的
“root”
标记)。我假设标记名是一个简单的
\w+
。应该对其进行更改以反映允许的标记名。正如您所看到的,解析树中可能有额外的空字符串,但您可以轻松地去掉它们

我知道,你的问题是关于PCREs的,但这有点像要求用合适的锤子来转动螺丝


顺便说一句,基于递归正则表达式引擎构建的解析器在理论上有一个缺点,这个缺点可能会变得非常现实:因为它们必须将每个输入元素重新扫描到其在树中的深度的倍,所以在没有回溯的情况下,最坏情况下的时间复杂度为O(n2)与Perl一样,PCRE可以将嵌套结构匹配到任意深度。下面是一个经过测试的脚本:

要匹配嵌套括号的正则表达式

当针对原始post中的测试数据运行时,以下是正则表达式匹配的内容:

示例输出:
找到了2个匹配项。

匹配[1]:[{tagname:content}]

匹配[2]:[{tag1:xnkudfdhkfujhkdjki diidfo现在嵌套的标记

[{tag2:more data here}]kj-udf}]

请注意,此正则表达式与最外层的一组可能嵌套的括号相匹配,并将括号之间的内容捕获到组
$1
中。如果希望解析出任何嵌套的括号,则需要在最外层括号的内容上递归地重新运行正则表达式,直到没有更多匹配为止

那些声称现代正则表达式引擎(如Perl、PCRE/PHP、.NET)不能解析嵌套结构的人完全是错误的。正则表达式已经很久很久没有“正则”了

编辑:2012-07-01 09:00请注意,此解决方案将嵌套括号与任何“任意深度”匹配,但始终受到系统内存、可执行堆栈大小和PHP
pcre.backtrack\u limit
pcre.recursion\u limit
memory\u limit
配置变量的限制。请注意,如果主题字符串太大和/或嵌套太深,则此正则表达式解决方案肯定会失败PHP/PCRE库甚至可能导致正在运行的可执行文件产生堆栈溢出、分段错误和程序崩溃!请参阅我对相关问题的回答,以深入讨论如何以及为什么会发生这种情况(以及如何避免和优雅地处理这类错误): 和
.

这需要正则表达式的递归功能。请发布预期结果好吗?我在第28行调用堆栈:0.0008 322672 1.{main}()C:\wamp\www\project1\~test.php中的php
Warning:preg\u match\u all():未知修饰符“\”,在C:\wamp\www\project1\~test.php:0.0008 3231762.preg\u match\u all()C:\wamp\www\project1\~test.php:28
但它仍然在regexBuddy中工作…我使用i和s作为修饰符
$p=preg\u match\u all('.\[{((?!}]\[{)。*+(?:\[{((((?!}]\[{)*+}](?:((?!}]]\\\\[{)*++.*++}]}]\\\\\\\\\[{is',$m,$m,$mat preg\u OFFSET\u捕获)
现代正则表达式引擎无法创建任意深度的解析树。@Walter Tross-也许是这样……但是上面的正则表达式解决方案是有效的!这取决于OP需要什么。如果他需要解析树,你的解决方案还没有提供。好吧……但这很好,因为我不需要解析长文件……我只需要解析一些html文件,它将包含这样的fews标记…它们也是由我制作的…无论如何ridgerunner谢谢…谢谢…我从你的编辑中看出你误解了我的评论。我说的是正则表达式引擎不能创建任意深度的解析树(这只是为了排除固定的最大深度情况),而不是它不能识别嵌套结构(parse在这里不是正确的词,因为它不能一次将它们分成所有部分)。很好的解决方案,但听起来像破解……无论如何,谢谢walter……我是初学者,我从你那里学到,我们可以做任何事,只需很少的思维和技巧。。。。。。thanks@MeghrajChoudhary当前位置我猜你指的是黑客。我最多称之为恶作剧。