【问题标题】:PHP get html comments in string and wrap in <pre> tag. Regex or DOM?PHP 在字符串中获取 html 注释并包装在 <pre> 标记中。正则表达式还是 DOM?
【发布时间】:2013-08-18 15:22:07
【问题描述】:

我想在&lt;pre&gt; 标记中不存在的字符串中查找注释标记,并将它们包装在&lt;pre&gt; 标记中。

似乎没有办法使用 PHP DOM '找到' cmets。

我已经在使用正则表达式进行一些处理,但是我非常不熟悉(尚未掌握或真正理解)正则表达式中的前瞻和后瞻。

例如我可能有以下代码;

<!-- Comment 1 -->

<pre>
    <div class="some_html"></div>
    <!-- Comment 2 -->
</pre>

我想将评论 1 包含在 &lt;pre&gt; 标记中,但显然不是评论 2,因为它已经位于 &lt;pre&gt; 中。

这通常在 RegEx 中如何完成?

以下是我对负面环视的理解,以及我的尝试,我显然做错了什么!

(?&lt;!&lt;pre&gt;.*?)&lt;!--.*--&gt;(?!.*?&lt;/pre&gt;)

【问题讨论】:

  • 这些“链接”都没有回答这个问题。我将提出一些我试图使问题更具体的内容。如果可能,我宁愿不使用外部库。
  • 您是否直接控制输入的 HTML,这样可以确保没有包含 &lt;pre&gt; 的 JavaScript 或 Comments,没有 CDATA 块,也没有嵌套的 cmets 或 &lt;pre&gt; 块?如果您不能确保这一点,则可能没有使用正则表达式的明智解决方案。如果可以的话,我会尽量给一个=)
  • @Joel 正则表达式的问题是,PCRE 不支持可变长度的lookbehinds。因此,虽然您的尝试实际上非常合理(除了一些贪心问题),但它只适用于 .NET。这就是为什么几乎不可能用正则表达式稳健地解决这个问题。

标签: php regex html


【解决方案1】:

如果您打算重用此代码,您应该真正使用 DOM 解析器。当使用真实的 HTML 呈现时,每一种正则表达式方法迟早都会失败。

话虽如此,这就是您可以(但不应该,见上文)做的事情:

首先,识别 cmets,例如使用

<!-- (?:(?!-->).)*-->

负前瞻块确保 .* 不会超出注释块。

现在,您需要确定此评论是否在 &lt;pre&gt; 块内。这里的关键观察是,在尚未包含在一个评论中的每条评论后面都有偶数个 &lt;pre&gt;&lt;/pre&gt; 元素。

因此,请始终以&lt;pre&gt;s 为一组,浏览其余文本,并检查您是否到达了末尾。

这看起来像

(?=(?:(?!</?pre>).)*(?:</?pre>(?:(?!</?pre>).)*</?pre>(?:(?!</?pre>).)*)*$)

所以,这将是

<!-- (?:(?!-->).)*-->(?=(?:(?!</?pre>).)*(?:</?pre>(?:(?!</?pre>).)*</?pre>(?:(?!</?pre>).)*)*$)

为只写代码欢呼 =)

这个表达式的主要组成部分是(?:(?!&lt;/?pre&gt;).),它匹配不是&lt;pre&gt;&lt;/pre&gt; 序列的起始括号的每个字符。

&lt;pre&gt; 上的允许属性和正确的转义留给读者作为练习。在RegExr 上查看此操作。

【讨论】:

  • 尽管您建议不要实际使用此方法,但我会将其标记为答案,因为它简洁地回答了我的问题:正则表达式,并且很有用。谢谢! :)
【解决方案2】:

似乎没有办法使用 PHP DOM '找到' cmets。

当然可以...使用PHP Simple HTML DOM Parser检查此代码:

<?php
$text = '<!-- Comment 1 -->

        <pre>
            <div class="some_html"></div>
            <!-- Comment 2 -->
        </pre>';

echo  "<div>Original Text: <xmp>$text</xmp></div>";

$html = str_get_html($text);

$comments = $html->find('comment');

// if find exists
if ($comments) {

  echo '<br>Find function found '. count($comments) . ' results: ';

  foreach($comments as $key=>$com){
    echo '<br>'.$key . ': ' . $com->tag . ' wich contains = <xmp>' . $com->innertext . '</xmp>';
  }
}
else
  echo "Find() fails !";
?>

$com-&gt;innertext 会给你像 &lt;!-- Comment 1 --&gt; 这样的 cmets...

您现在只需按照自己的意愿清洁它们。例如使用&lt;!--\s*(.*)\s*--&gt;...试试HERE

编辑:

只是关于后视的说明,它必须具有固定宽度,因此您不能使用重复 *+ 或可选项目 ?

坏消息是大多数正则表达式风格不允许您在后视中使用任何正则表达式,因为它们不能向后应用正则表达式。因此,正则表达式引擎需要能够计算出在检查lookbehind之前要退后多少步。

因此,许多正则表达式风格,包括 Perl 和 Python 使用的那些,只允许固定长度的字符串。您可以使用可以预先确定匹配长度的任何正则表达式。这意味着您可以使用文字文本和字符类。您不能使用重复项或可选项。您可以使用交替,但前提是交替中的所有选项都具有相同的长度。

来源:http://www.regular-expressions.info/lookaround.html

【讨论】:

  • 使用简单的html dom外部库,是的。不使用原生 PHP DOM 类
【解决方案3】:

Xpath 是你的朋友:

$xpath = new DOMXpath($doc);

foreach($xpath->query('//comment()[not(ancestor::pre)]') as $comment){
  $pre = $doc->createElement("pre");
  $comment->parentNode->insertBefore($pre, $comment);
  $pre->appendChild($comment);
}

【讨论】:

    【解决方案4】:

    这很简单,使用称为堆栈计数器的原理,
    基本上您计算&lt;pre&gt; 标记的数量和&lt;/pre&gt; 标记的数量,直到HTML 代码中放置您的段为止。
    如果&lt;pre&gt;&lt;/pre&gt;更多 - 这意味着“&lt;pre&gt;..--you are here--..&lt;/pre&gt;”。
    在这种情况下,只需返回匹配项,未修改 - 就这么简单。

    【讨论】:

      猜你喜欢
      • 2021-03-15
      • 2015-07-19
      • 1970-01-01
      • 1970-01-01
      • 2011-06-26
      • 2021-09-28
      • 2014-09-23
      • 2014-03-29
      • 1970-01-01
      相关资源
      最近更新 更多