用正则表达式递归替换匹配标记


Recursive replacement of matching tags with regular expressions

>我有以下字符串:

<?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);

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

因此,创建一个堆栈(一个数组,您可以在其中添加和删除末尾array_push() array_pop()的项目。

遍历标签,在堆栈上推送已知的开始标签。

当您找到结束标签时,弹出堆栈,这将告诉您需要关闭的标签。

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

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;
}

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