以下摘自某网友来信:
难点
- 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的字串,欢迎留言讨论。