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

3
我有以下字符串: <?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
我的眼睛...它们在流血... - Vivin Paliath
我已决定放弃 <?/?> 结束标签并始终要求用户为结束标签命名。这将使我更容易解析它们,而且肯定会快得多,因为我不需要手动浏览它们。 - romaninsh
2个回答

1

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

所以创建一个栈(一个数组,在其中从末尾添加和删除项,使用 array_push() array_pop() )。

遍历标签,将已知的开放标签推入堆栈。

当你到达闭合标签时,弹出堆栈,这将告诉你需要关闭的标签。


因为你需要一个解析器来解析任意嵌套的层次结构。https://dev59.com/X3I-5IYBdhLWcg3wq6do - Byron Whitlock

0
对于递归结构,请制作一个递归函数。在某种伪代码形式中:
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;
}

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

这应该匹配任何标签,预定义所有可能的标签不是选项。此外,如果文本包含<(而不是<?),它也将失败。 - romaninsh
递归使用调用栈。迭代使用隐式栈。 - Byron Whitlock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接