使用正则表达式删除注释

以下摘自某网友来信:
在这里插入图片描述

难点

  • Javascript 不支持点号匹配换行符, 因此无法直接进行多行匹配
  • 处理前面没有 http://, 当然要用否定前瞻( negative lookbehine)了:(?<!http:)\/\/. 可惜 javascript 不支持

在这里插入图片描述

思路

关于多行匹配

这个问题, 之前我已经说过, 要点是使用[\S\s]来模拟匹配换行符的点号。原文在这里:《DIY万能通配符》. 可以以此写出这样的 JS 代码来消除多行注释:

//to uncomment C-style multiple line comment
function uncomment_multi(str)
{
     return str.replace(/\/\*[\S\s]*?\*\//g, "");
}

单行注释之 JS 实现(不完善)

单行注释并没有想像中的那样简单. 如果你认为只要 str.replace("//.*$")即可, 那么必须保证所要处理的文本都是最简单的, 如下:

var pig="ase"; //this is a comment.

事实上这是行不通的.现实程序中下面的例子比比皆是:

var url="http://iregex.org"; //this is my site.
var url="//not real comment here http://iregex.org"; //this is my site.

我尝试使用 JS 写了个模拟否定前瞻的函数, 可以处理http://这种情况, 但是该函数看起来并不令人赏心悦目, 而且也不能处理引号中有双斜杠的情况. 我对 JS 的正则式支持的特性之简陋实在很失望。于是,我求助于 perl 完成这一任务. 先看一下我写的 JS 的删除单行注释的函数:

function uncomment_single(str)
{
    var result;
    var single=new RegExp("\/\/.","ig");
    var start=0;
    while((result=single.exec(str))!=null)
    {
        var part=str.slice(start,result.index);
        var negLeft=new RegExp("http:$","i");
        if (! negLeft.test(part))
        {
            return str.slice(0,result.index);
        }
        start=result.index+result[0].length-1;
    }
    return str;
}

Perl 版删除注释思路及源码(相对完善)

待测试文本

好吧, 既然祭出了强大的 Perl, 之前的小打小闹似的例子就一边去吧. 我将使用如下相对复杂的文本来验证我的程序:

<!DOCTYPE h/tml PUBLIC "-//W3C//DTD XHTML\" 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> sdfasdf//real comment here//"

认真分析单行注释的特点

正确地分析其特点, 是写出合理高效的程序的前提. 观察可知, 单行注释的特点如下:

  • 引号内(包括单引号和双引号)的双斜线不算注释.
  • 引号是配对出现的, 两个引号之间的以反斜线转义掉的引号不算结束符. 例如"hello \" //world", 这里的//world部分不能算做注释.
  • 由连续的非引号非斜线部分组成的字符串也不是注释。特别指出,单个斜线不能算做注释。为什么前半部分不但要非引号而且要非斜线呢?因为[^'"]+是有可能误匹配abcde//real comment "quoted string in comment"这样的情况, 因此我们归纳出一个条件[^'"/]+; 又因为还要避免abcde/real comment "quoted string in comment"这样的情况,还需要特别补充规定单个的斜线不是注释。正则式是[^'"/]|(?<!/)/(?!/)
  • 除去上述内容以外, 以双斜线开始直至行尾的部分就是注释. 因为我们用到了行尾这个概念, 需要在正则式中特别指出是^$匹配行首行尾的多行模式. 使用//m来表示.

正则实现

#!/usr/bin/perl -w
$str = <<"EOF";
<!DOCTYPE h/tml PUBLIC "-//W3C//DTD XHTML\" 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> sdfasdf//real comment here//"
EOF
#print $str;
if ($str=~
    m%
        ^
            (?:
                [^'"/]|
                (?<!/)/(?!/)|
                (?<quote>['"])
                    (?:\\ \g{quote}|
                    (?!\g{quote}).)*
                \g{quote}
            )*
            (?<comment>//.*)
        $
    %xm)
    {
        print $+{comment};
}

几点补充

  • 该程序在 Perl 5.10 版才能运行成功. 因为用到了命名捕获(?<quote>['"])这样比较高阶的特性。当然,不使用 5.10 也并非没有办法, 我们大可以使用 numbered capture,只不过看起来更不直观罢了。
  • 匹配结束后,命名捕获都保存在 hash 表%+中了。使用print $+{comment}这样的方式可以方便地调用
  • 指定了 x 模式, 以便加入空白字符和换行, 让正则表达式看起来有层次感。事实上对于复杂的正则表达式,不使用 x 模式是极其不明智的做法。
  • 为了在字串中方便地表示单双引号, 使用了 heredoc 的方式. 个人觉得不如 python 的三重引号方便。

小结

从正则表达式的角度来说, JS 实在太弱。当然,也与本人的 JS 功底较浅有关系。Perl 对于正则表达式的支持实在是强撼且不遗余力。上面的实现,应该可以涵盖绝大多数的注释情况了。如果您测试出现 bug, 或者遇到更 BT 的字串, 欢迎留言讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sp42a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值