当前位置:

使用正则表达式删除注释

访客 2024-01-09 1358 0

以下摘自某网友来信:

难点

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

思路

关于多行匹配

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

//touncommentC-stylemultiplelinecommentfunctionuncomment_multi(str){returnstr.replace(/\/\*[\S\s]*?\*\//g,"");}

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

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

varpig="ase";//thisisacomment.

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

varurl="http://iregex.org";//thisismysite.varurl="//notrealcommentherehttp://iregex.org";//thisismysite.

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

functionuncomment_single(str){varresult;varsingle=newRegExp("\/\/.","ig");varstart=0;while((result=single.exec(str))!=null){varpart=str.slice(start,result.index);varnegLeft=newRegExp("http:$","i");if(!negLeft.test(part)){returnstr.slice(0,result.index);}start=result.indexresult[0].length-1;}returnstr;}

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

待测试文本

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

<!DOCTYPEh/tmlPUBLIC"-//W3C//DTDXHTML\"1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">sdfasdf//realcommenthere//"

认真分析单行注释的特点

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

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

正则实现

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

几点补充

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

小结

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

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~