(php) 正则表达式删除注释但忽略字符串中的出现

Posted

技术标签:

【中文标题】(php) 正则表达式删除注释但忽略字符串中的出现【英文标题】:(php) regexto remove comments but ignore occurances within strings 【发布时间】:2011-01-29 08:56:15 【问题描述】:

我正在写一个评论剥离器,并试图在这里满足所有需求。我有下面的代码堆栈,它删除了几乎所有的 cmets,但它实际上走得太远了。很多时间都花在尝试、测试和研究匹配的正则表达式模式上,但我并不认为它们在每个方面都是最好的。

我的问题是我也有“php cmets”(在标准代码甚至 PHP 字符串中并不是真正的 cmets),我实际上并不想删除。

例子:

<?php $Var = "Blah blah //this must not comment"; // this must comment. ?>

最终发生的事情是它虔诚地剥离,这很好,但它留下了某些问题:

<?php  $Var = "Blah blah  ?>

还有:

也会导致问题,因为注释会删除该行的其余部分,包括结尾?>

看到问题了吗?所以这就是我需要的......

需要忽略 '' 或 "" 中的注释字符 在同一行中使用双斜杠的 PHP 注释应该只删除注释本身,或者应该删除整个 php 代码块。

这是我目前使用的模式,请随时告诉我是否可以对现有模式进行改进? :)

$CompressedData = $OriginalData;
$CompressedData = preg_replace('!/\*.*?\*/!s', '', $CompressedData);  // removes /* comments */
$CompressedData = preg_replace('!//.*?\n!', '', $CompressedData); // removes //comments
$CompressedData = preg_replace('!#.*?\n!', '', $CompressedData); // removes # comments
$CompressedData = preg_replace('/<!--(.*?)-->/', '', $CompressedData); // removes html comments

您能给我的任何帮助将不胜感激! :)

【问题讨论】:

【参考方案1】:

如果要解析 PHP,可以使用token_get_all 获取给定 PHP 代码的 tokens。然后你只需要迭代标记,删除评论标记并将其余部分重新组合在一起。

但是您需要一个单独的 HTML cmets 过程,最好也有一个真正的解析器(如 DOMDocument 提供 DOMDocument::loadHTML)。

【讨论】:

虽然,大多数“HTML”解析器实际上是 XML 解析器,并且无法正确解析 PHP 常用的 HTML,因为文件本身的格式很少(即使生成的页面是)。 这就是为什么 DOMDocument 有 loadHTML 方法的原因,它可以理解完全混乱的 HTML。 DOMDocument 与查找所有 cmets 并删除它们的 na xpath 表达式组合似乎是 HTML cmets 的有效选项。另外,它使生成的 HTML XHTML 兼容。【参考方案2】:

您应该首先仔细考虑您是否真的想要这样做。尽管您正在做的事情可能看起来很简单,但在最坏的情况下,它会变成极其复杂的问题(只需几个正则表达式即可解决)。让我仅举例说明当您尝试从文件中去除 HTML 和 PHP cmets 时会遇到的几个问题。

你不能直接剥离 HTML cmets,因为你可能在 HTML cmets 中有 PHP,比如:

<!-- HTML comment <?php echo 'Actual PHP'; ?> -->

你也不能简单地单独处理 &lt;?php?&gt; 标签内的东西,因为结尾的 thag ?&gt; 可以在字符串甚至 cmets 内,例如:

<?php /* ?> This is still a PHP comment <?php */ ?>

别忘了,?&gt; 实际上结束了 PHP,如果它前面有一行注释的话。例如:

<?php // ?> This is not a PHP comment <?php ?>

当然,就像您已经说明的那样,字符串中的注释指示符会有很多问题。解析字符串以忽略它们也不是那么简单,因为您必须记住引号可以被转义。喜欢:

<?php
$foo = ' /* // None of these start a comment ';
$bar = ' \' // Remember escaped quotes ';
$orz = " ' \" \' /* // Still not a comment ";
?>

解析顺序也会让你头疼。您不能简单地选择先解析单行 cmets 还是先解析多行 cmets。它们都必须同时解析(即按照它们在文档中出现的顺序)。否则你可能会得到损坏的代码。让我举例说明:

<?php
/* // Multiline comment */
// /* Single Line comment
$omg = 'This is not in a comment */';
?>

如果您首先解析多行 cmets,则第二个 /* 将占用部分字符串,从而破坏代码。如果你先解析单行 cmets,你最终会吃掉第一个 */,这也会破坏代码。

如您所见,如果您打算使用正则表达式解决问题,则必须考虑许多复杂的场景。唯一正确的解决方案是使用某种 PHP 解析器,例如 token_get_all(),对整个源代码进行标记,去除注释标记并重建文件。恐怕这也不完全简单。它对 HTML cmets 也无济于事,因为 HTML 未被触及。您也不能使用 XML 解析器来获取 HTML cmets,因为 HTML 很少用 PHP 形成良好的格式。

简而言之,您正在做的事情的想法很简单,但实际实现比看起来要困难得多。因此,我建议尽量避免这样做,除非你有充分的理由这样做。

【讨论】:

所有非常好的观点,我已经自动考虑了这些情况并取得了总体成功。正如我在下面的评论中所说,我将其用于内部目的,因此它不必是完美的。我已经设法解决了我的大部分问题,我现在唯一的方法是删除换行符 - 我 DON'T 想要删除字符串中的换行符。看,这通常是针对我自己的编码风格,所以我知道我如何评论事物等,并且我已经相应地使用了 reg-ex。在我所有的测试中,现在一切都很好。 :) 除了“有意的”换行符。【参考方案3】:

在 REGEX 中执行此操作的一种方法是使用一个复合表达式和 preg_replace_callback

我打算发布一个糟糕的示例,但最好的地方是查看 Dean Edwards 的 JS 打包脚本的 PHP 端口的源代码 - 你应该看到总体思路。

http://joliclic.free.fr/php/javascript-packer/en/

【讨论】:

这只是为了在单个脚本中对 HTML、JS 和 PHP 进行内部压缩,性能不是问题。事实上,它出奇地快,尽管我知道 REGEX 替换本身并不是执行此操作的最佳方式。我已经设法让事情按我的意愿工作,但现在我需要得到它,以便它删除任何 /n 换行符,除非它们包含在“”或“”中。有什么线索吗? Dean 的包装工可能无法帮助我解决这个特定问题。不过这可能很简单……我在这方面有点n00b,呵呵,这主要是我的实验。【参考方案4】:

试试这个

private function removeComments( $content )
    $content = preg_replace( "!/\*.*?\*/!s" , '', $content );
    $content = preg_replace( "/\n\s*\n/" , "\n", $content );    
    $content = preg_replace( '#^\s*//.+$#m' , "", $content );
    $content = preg_replace( '![\s\t]//.*?\n!' , "\n", $content );
    $content = preg_replace( '/<\!--.*-->/' , "\n", $content );
    return $content;

【讨论】:

以上是关于(php) 正则表达式删除注释但忽略字符串中的出现的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式 - 获取引号中的字符串忽略转义的引号和评论

如何在 PHP 中注释掉正则表达式的 */ 部分

正则表达式替换但忽略最后一个字符

PHP:正则表达式替换,同时忽略html标签之间的内容

用于查找字母字符的前 x 个出现的正则表达式,忽略其他所有内容

PHP正则表达式基本函数