精通正则表达式之重复单词程序详解
March 20th, 2010
Categories: 问答
见chinaunix论坛上有这样一问:
搜索重复单词(“this this”)问题,问为什么第4行要加^:
1 2 3 4 5 6 7 | $/ = ".\n"; while (<>) { next if !s/\b([a-z]+)((?:\s<<[^>]+>)+)(\1\b)/\e[7m$1\e[m$2\e[7m$3\e[m/ig; s/^(?:[^\e]*\n)+//mg; # Remove any unmarked lines. # 为何需要加^ s/^/$ARGV: /mg; # Ensure lines begin with filename. print; } |
要解答这个问题,不能只print "hello world";着眼于这一行。程序不长,从头到尾详细读一下,这个问题就兵不血刃地解决了。
$/ = ".\n";这一行的作用是设置分行符. 如果在程序中使用了while(<>)这样的东东, 就可以使用perl script.pl file.txt这样调用该perl脚本了, 它源源不断地读入file.txt中的内容.$/是分行符, 其默认值是newline. 默认情况下, 每个while的循环处理file.txt中的一行. 如果将$/定义为undef, 即在程序开头undef $/一下, file.txt中的全部内容就都读入到一个变量$_中去了.
本程序中, 换行符定义为.\n, 其含义就是, 将以点号后跟一个\n视为换行符. 这样就忽略了普通的折行的情况, 使程序更加智能, 可以处理一行行尾是this, 下行行首还是this的重复词的情况. 关于$/的详细资料, 请参考perl 文档.while (<>) {这一行无须细说.next if !s/\b([a-z]+)((?:\s|<[^>]+>)+)(\1\b)/\e[7m$1\e[m$2\e[7m$3\e[m/ig;这一行需要说一下. 其框架虽然是next if !s///, 看起来什么也没做似的, 但是一点儿都没偷懒. 它先判断该行是否匹配某模式, 如果匹配则进行替换(至于如何替换, 下文交待); 如果不匹配, 才提前结束本次循环, 进入下一次循环.
现在再来看一下它是如何进行替换的. 看上去很复杂, 但是并不是不可理解. 我们先去掉\e[m之类的东东, 这是在bash下对字符进行高亮控制的.
s/\b([a-z]+)((?:\s|<[^>]+>)+)(\1\b)/$1 $2 $3/ig;
这样是不是清晰多了? 后边的$1, $2, $3不去管它, 我们只看一下匹配部分的正则式:
\b([a-z]+)((?:\s|<[^>]+>)+)(\1\b)
左边界符; 一个英文单词; 一个或多个空白字符或<...>; 前边出现过的那个英文单词再次出现; 右边界符.
很清晰嘛!s/^(?:[^\e]*\n)+//mg; # Remove any unmarked lines. # 为何需要加^
这一句很简练. 它的作用是, 将没有出现重复词的行去掉. 请注意, 这里的行, 是指逻辑行, 而非物理行. 程序一开始就说了, 以.\n结尾的才算一行(物理行).
^(?:[^\e]*\n)这是指整个逻辑行中, 从行首到\n, 全是由0个或多个非\e组成, 即该行未被插入高亮控制字符, 亦即该行原来不存在重复词.
本正则替换语句使用的模式是mg, 即^匹配中间的逻辑行的行首; 且整个字串或许包含多个逻辑行, 因此使用g来进行全局替换.s/^/$ARGV: /mg;这是让输出结果的每一行的行首(同样是逻辑行), 插入文件名和冒号. 匹配模式mg的含义同上.
print;这是打印输出默认变量$_的值. 上面所有没有出现”主语”的, 都默认是对$_进行操作. 地球人都知道.
现在再来看一个例子. 对于文本文件a.txt:
1 2 3 4 5 6 | this is a apple. this is a red red apple. i love regex i love regular expressions this is a fat <very>fat</very> pig. |
它是怎么被程序处理的呢?
首先跟据$/的设定, 文本文件被分为这样几行:
-
1this is a apple.
-
1this is a red red apple.
-
1
2
3
4i love regex
i love regular expressions
this is a fat
<very>fat</very> pig.
为什么后三行被划到一起呢? 因为分行符是.\n.
处理步骤:
- 程序在处理第一组
1this is a apple.
时, 直接next过, 不予操作;
-
1this is a red red apple.
red red被高亮, 打印输出.
-
1
2
3
4i love regex
i love regular expressions
this is a fat
<very>fat</very> pig.fat fat被高亮; i love regex以及i love regular expressions被
s/^(?:[^\e]*\n)+//mg击中, 分别删除(替换为空).
解释完毕.
Leave a comment
| Trackback