递归更换匹配标签的正则表达式

问题描述:

我有以下字符串:递归更换匹配标签的正则表达式

<?foo?> <?bar?> <?baz?> hello world <?/?> <?/?> <?/?>

我需要一个正则表达式将其转换成

<?foo?> <?bar?> <?baz?> hello world <?/baz?> <?/bar?> <?/foo?>

下面的代码适用于非 - 递归标签:

$x=preg_replace_callback('/.*?<\?\/\?>/',function($x){ 
    return preg_replace('/(.*<\?([^\/][\w]+)\?>)(.*?)(<\?\/?\?>)/s', 
      '\1\3<?/\2?>',$x[0]); 
},$str); 
+1

我的眼睛......他们正在流血...... – 2011-05-13 01:31:57

+0

我已决定放弃/?>结束标记并始终要求用户命名结束标记。这会让我更容易解析它们,速度也更快,因为我不需要依靠手动进行解析。 – romaninsh 2011-05-20 09:42:25

你不能用正则表达式来做到这一点。你需要编写一个解析器!

因此,创建一个堆栈(从最后添加和删除项目的数组,使用array_push()array_pop())。

遍历标签,推动栈上已知的开启标签。

当你到一个结束标签时,弹出堆栈,它会告诉你需要关闭的标签。

+0

为什么不能使用(?R)? – romaninsh 2011-05-13 01:45:13

+0

因为您需要解析器来解析任意的嵌套层次结构。 http://*.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags – 2011-05-13 05:13:30

对于递归结构,请做一个递归函数。在某种形式的伪代码中:

tags = ['<?foo?>', '<?bar?>', '<?baz?>'] 

// output consumed stream to 'output' and return the rest 
function close_matching(line, output) { 
    for (tag in tags) { 
    if line.startswith(tag) { 
     output.append(tag) 
     line = close_matching(line.substring(tag.length()), output) 
     i = line.indexof('<') 
     ... // check i for not found 
     output.append(line.substring(0, i)) 
     j = line.indexof('>') 
     ... // check j for error, and check what's between i,j is valid for close tag 
     output.append(closetag_for_tag(tag)) 
     line = line.substring(j + 1) 
    } 
    } 
    return line; 
} 

这应该会给你一个基本的结构。

+0

这应该匹配任何标签,预先定义所有可能的标签不是选项。此外,这也会失败,并且包含 romaninsh 2011-05-13 01:57:19

+0

递归使用调用堆栈。迭代使用隐式堆栈。 – 2011-05-13 05:03:15