关于从普通文本提取正则表达式的再思考
Mar 9th
rex按:写完上一篇文章之后,一直在考虑如何真正实现从普通文本中归纳正则表达式的实现。走了许多弯路,也学了不少知识。例如,perl黑豹书上复杂的数据结构、匿名散列和数组、refenrence;紫龙书上的状态机的构造,数据结构上图论的知识,都是很有用的。另外还新学了graphviz的用法。以前觉得很神秘,不过一用才发现很直观。本文的插图是使用online版本的graphviz画的。
除了本文的这种实现方法(基于图),我还使用另一种方式实现了,很简单:基于关键词。具体作法是,逐一读取每一行文本,使用\s+等将其split开,形成array;然后再对所有的array进行求交集的操作(使用hash),得到每一行都有的关键词;然后按从左到右的顺序建立这覆的正则式^(.*?)keyword1(.*?)keyword2….keywordN(.*?)$,再分别匹配每一行文本,得到hash的hash表,或者array的array,转置,并列输出,得到^(option1|option2…)keyword1(option..)…$这样的正则式。最后作为验证,再将所最终生成的正则与每一行匹配测试一下。
这样以词为单位做完之后,再逐个字母地分隔开来,递归地处理(option1|option2…)的部分。先是单词级,再是字母级,有利于先在最大程度上找出重复的内容;而且粗化和细化的处理过程,思路是一致的,粒度不同罢了。
新手请自重,高手请赐教,我的思路未必是正确或最优的。
问题
this is a red foxthis is a blue firefoxthis is a piga red fox
标准正则
有两种极端的解法是不可取的:
- ^.*$
- ^(this is a red fox|this is a blue firefox|this is a pig|a red fox)$
第一种失之于太宽泛,第二种失之于太狭隘。太宽泛则泥沙俱下,无论什么文本都能匹配;太狭隘则僵化死板,缺乏灵活性。好的正则表达式源于例文本(从例文本中提取规律),又高于例文本(能匹配同规律的其它文本)。匹配什么,排除什么,都有定则,所谓“君子有所为而有所不为”,指的就是这种情况(貌似跑题了:))。
思路
既然是一条正则匹配所有的文本,那么这条正则(记为$re)也应该匹配第一行文本。
第一行文本为this is a red fox。那么,从^this is a red fox$应该是$re的一个(真)子集。它的路径为:“^”->this->is->a->red->fox->”$”。全部节点之间,是串联关系,从左到右依次排列即可。
示意图如下(可以点击看全尺寸图,下同):
同理,第二行文本也应该是$re的子集。不过,由于已经存在了由^->this->is->a的路径,到a时出现支路,a->blue->firefox->$;
将此路径添加到示意图上,得到:
显而易见,这两条并列的支路,始于a,终于$,可以使用|来并列之。
好了,我们总结一下规律:
再往下,this is a pig,同理,只需要在原图基础上添加a->pig->$的支路即可。此时图示如下:
此时,观察可知,一种新的情况出现了。同时存在^->a,和“^”->this->is->a两条路径。想一下初中物理电路图,我们可以将这种情况称为“短路”,即,“^”->this->is->a这个线路的^、a两个节点之间,添加了一条无障碍通道,它能无视this、is的存在,因此,让this->is这条路径成为可选项。再总结一下规律:
如果有A->B->…C->D的路径,且有A->D的路径,则称A->D之间存在短路,此时,B->…->C可以用(B->…->C)?来表示(就是用括号来表示被短路的部分,问号表示短路之)。
这也就是为什么上文要强调是一个节点的缘故。
实现
…
上图其实是一个有向图,只需记录所有的顶点集合,路径集合,再来求各路径之间的关系;最后打印输出,即是所求。
这两个集合在读取文本文件行的时候可以一次性建立。不复杂。关键是关系的确立。
- 从一个顶点A出发的N条支路必定汇合(只是有时是同一个点,有时不在同一点而已。本文给出的例子是最简单的情况,这里可以假设为汇合到同一点)于M点。
- 这N条路中,每一条路径的长短以经过的节点个数来计算。例如上图中,^到a有一条路,上面的路径为2,下面的路径为0。
- 短的支路决定了这N条支路的关系。
- 长度为任意两点之间,最多只可能有一条长度为0的边。
- 如果存在长度为0的边,则其余的同级的支路被短路。
- 长度不为0的N-1条支路之间是并列关系。
- 整个图始于^,终于$。
个人应用之明文字串到正则
Feb 10th
近来工作中需要将某种明文字串转为简单的正则式。手动做当然可以,但是大量重复性的劳动,自然是交给机器处理为好。昨晚写了一款这样的脚本,放在这里。因为是处理我自己的工作的脚本,贴在这里仅作记录和存档之用,可能对别人没什么实际作用。当然,从现有的明文字串到正则式的转换,应该是个不错的题目,有兴趣朋友的可以深究。
值得一提的是,代码中用了$&, (?{}) 这样的perl only的东东,明晰了思路,简化了代码。如果不使用这种特性的话,代码要长5倍。另外,据说从效率上来说,use English之后,使用$MATCH比直接使用$&快5倍。但是对于即输入即执行的命令行程序来说,$&已经足够好。
实际应用一例:
perl hash2re.pl H:aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA0.zip/H:aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA-0/aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA-0/aaa/Aaaaa/aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA-0.exe
RE 1: ^[a-z]{3}-[A-Z][a-z]{3}-[A-Z][a-z]{3}[A-Z][a-z]{6}[A-Z][a-z]{7}-[A-Z]{3}[0-9]\.zip$
Matches: "aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA0.zip"
RE 2: ^[a-z]{3}-[A-Z][a-z]{3}-[A-Z][a-z]{3}[A-Z][a-z]{6}[A-Z][a-z]{7}-[A-Z]{3}-[0-9]$
Matches: "aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA-0"
RE 3: ^[a-z]{3}-[A-Z][a-z]{3}-[A-Z][a-z]{3}[A-Z][a-z]{6}[A-Z][a-z]{7}-[A-Z]{3}-[0-9]$
Matches: "aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA-0"
RE 4: ^[a-z]{3}$
Matches: "aaa"
RE 5: ^[A-Z][a-z]{4}$
Matches: "Aaaaa"
RE 6: ^[a-z]{3}-[A-Z][a-z]{3}-[A-Z][a-z]{3}[A-Z][a-z]{6}[A-Z][a-z]{7}-[A-Z]{3}-[0-9]\.exe$
Matches: "aaa-Aaaa-AaaaAaaaaaaAaaaaaaa-AAA-0.exe"源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #!/usr/bin/perl # by rex zhang # Feb 09 2010 in Shanghai # usage: split and regexize hashed filename # my $lines=$ARGV[0]; while($lines =~ m#(C:[^/]+)#) { $c=$1; $lines =~ s/$c//; print "ClearText Filename Ignored:\t\"$c\"\n\n"; } my @array=split(m!\s*(?:\/|H:)+\s*!, $lines); my $counter=0; foreach $line (@array){ next if not $line; my $re=$line; local $len; $re =~ s/(?=[.\[\]()])/\\/g; $re =~ s/\?/./g; $re =~ s/0+(?{ $len=length($&)})/[0-9]\{$len\}/g; $re =~ s/A+(?{ $len=length($&)})/[A-Z]\{$len\}/g; $re =~ s/a+(?{ $len=length($&)})/[a-z]\{$len\}/g; $re =~ s/(.)\1+(?{ $len=length($&)})/$1\{$len\}/g; $re =~ s/\{1\}//g; $re = "\^$re\$"; $counter++; if ($line =~ /$re/) { print "RE $counter:\t$re\n"; print "\tMatches: \"$line\"\n"; } else { print "RE $counter:\t$re\n"; print "\tFailed: \"$line\"\n"; } print "\n"; } |
统计重复文本行的两种方法
Feb 6th
假设样本文件a.txt内容如下:
hello world! hello world! I love regex. hello world! I love regex. hello world!
简单观察可知,hello world!共重复4行;I love regex.重复2行。如何使用正则表达式来写一个程序,统计这些数据呢?因为现实中需要统计的文件,绝非是只凭肉眼就能观察出来。我想到了两种方法,第一种方法,是依赖于正则表达式(否则这篇文章也不会贴在这里);第二种,hash表做主角,正则表达式作绿叶。
正则表达式的解法
思路是:对于任何一行文本,如果后面若干行[0~EOF)之后,如果存在相同的文本行,则记下该行内容,统计出现次数;然后删除这样的文本行,再进行下一行的统计。输出统计结果。
下面是相应的perl程序,附注释。
#!/usr/bin/perl #usage: ./dup_re.pl <a.txt undef $/; # enable "slurp" mode my $file = <STDIN>; # whole file now here while($file =~ m / #for each line; ^\s* #ignore the whitespaces at both ends; (\S.*?) #get the line content, save to $1; \s*$ #ignore empty lines by using \S .*? #check if there is the same pattern of $1 ^\s*\1\s*$ #after 0 or more lines; /smx) { my $line=$1; my $count= $file =~ s/$line//g; #delete the duplicated lines #save the number to $count; #ignore empty lines print $count,"times:\t",$line,"\n"; }
Hash表解法
这种方法,受益于perl语言本身的强大的hash表功能。思路如下:
- 建立空的hash表;
- 逐行读取文件;
- 以文本内容为key,插入到表中来。如果是首次出现,value为0,否则value++。
- 输出hash表中value>=2的记录。
Perl程序:
#!/usr/bin/perl #usage: ./dup_hash.pl a.txt my %hash=(); while(<>) { if (/^\s*(\S.*?)\s*$/) #ignore whitespaces at both ends; { #ignore empty lines by using \S $hash{$1}++; #save the line to $1, and count the time it appears } } #sort the hash by values; foreach $key (sort { $hash{$b} <=> $hash{$a} } keys %hash) { if ($hash{$key}>=2) #only print the lines that duplicates; { #for all results, just remove the 'if' line printf "%d times:\t%s\n", $hash{$key}, $key; } }
结果
上面的程序分别保存为dup_re.pl,dup_hash.pl。由于程序对于外部文件的读取的方法不同,运行方式也有差别,详见下图:

Update
忽然想到,如果要让这脚本更有效,可以指定忽略大小写,忽略单词间多个空格的情况,使得Hello world!与 hello WORLd! 被视为重复行。测试了一下,正则式没让我失望。
[译]递归正则表达式
Dec 16th
原文在此。rex译于2009年12月15~17日,翻译过程中使用的是google docs@prism@firefox@ubuntu 9.10,很爽的体验。感谢余晟老师在正则和翻译方面的悉心指导。
平时我们用到的正则表达式,其实没那么“规则”。多数编程语言所支持的扩展的正则表达式,其运算能力比起形式语言理论所定义的“规则”正则表达式要强得多。
rex注:在这里其实可以看到,如果将正则表达式译为正规表达式,一切就都通顺了:
平时我们用到的正规表达式,其实没那么“正规”。多数编程语言所支持的扩展的正规表达式,其运算能力比起正式语言理论所定义的“正规”正规表达式要强得多。regular expression的日文是“正规表现”,在鸟哥书中,好像也将其称为正规表达式。via
例如,经常用到的捕获缓存,就是用来帮助我们临时存储任意正则表达式模式,以便重复使用。 又如,“环视断言”能让正则表达式引擎在做决定之前先偷偷看看环视一下。这些扩展让正则表达式非常强大,足以描述一些“上下文无关语法”。
Perl语言的正则表达式引擎特性异常丰富,其特征之一是懒惰正则子表达式(Lazy regular subexpressions),格式为(??{code}),其中的“code”可以是任意一段perl程序,该子表达式可能匹配时,这段程序就会执行。
我们可以利用这一特征来编写出非常有趣的东西,即将正则表达式自身嵌在它的“code”部分,由此生成递归的正则表达式(a recursive regular expression)!
一直以来,正则表达式无法匹配0n1n这种表达式,也就是由若干个0以及同等数量的1所组成的字符串。如果使用懒惰正则子表达式,这一经典问题就迎刃而解。
下面是匹配0n1n字串的perl正则表达式代码。
$regex = qr/0(??{$regex})?1/;
rex注:紫龙书第四章有云:“文法是比正则表达式表达能力更强的表示方法。每个可以使用正则表达式描述的构造,都可以使用文法来描述,但是反之不成立。换句话说,每个正则语言都是一个上下文无关语言,但是反之不成立。”书中交待的正则,也应该是指的常规正则表达式,而非现代语言中的扩展的正则表达式。一般使用正则表达式来构造小部件,而使用文法来组建语言框架。
此正则表达式匹配一个字符0,之后是正则表达式自身0或1次,之后是字符1。如果正则表达式自身部分不能匹配,那么它只能匹配01;如果自身部分可以匹配,则正则表达式匹配的是00($regex)?11,此时若不能匹配自身则结果是0011,若可以匹配就是000($regex)?111,……依次顺延。
下面是匹配050000150000的Perl程序:
1 2 3 4 5 6 7 8 9 10 11 | #!/usr/bin/perl $str = "0"x50000 . "1"x50000; $regex = qr/0(??{$regex})*1/; if ($str =~ /^$regex$/) { print "yes, it matches"; } else { print "no, it doesn't match"; } |
现在来看题图所示的Yo Dawg正则表达式。你先猜猜它的作用?正确答案是,它匹配(foo(bar())baz)这样完全嵌套的括号表达式(fully parenthesized expression)或((()()())())这样的平衡括号表达式(balanced parentheses)。
1 2 3 4 5 6 7 8 9 | $regex = qr/ \( # (1) match an open paren ( #(1),此处匹配开括号 ( # followed by #(2),紧接着是 [^()]+ # (3) one or more non-paren character #(3),1个或多个非括号字符 | # OR #(4),或 (??{$regex}) # (5) the regex itself #(5),正则式自身 )* # (6) repeated zero or more times #(6),重复0或多次 \) # (7) followed by a close paren ) #(7),紧接着是闭括号 /x; |
rex注:关于Yo Dawg图片的含义,可以参考这里。基本上是全是“Yo dawg, I herd you like X, so we put a Y in your Y so you can Z while you Z”的结构的配图文字。
构造此正则表达式的思路是这样的。对于完全嵌套的括号表达式,它的开始字符是一个开括号。这是最简单的一步,我们直接写出(上面程序中的(1))。同理,它的结束字符是闭括号,于是得到(7)。现在该动脑筋了,括号中间是什么呢?对,它可以是既不是开括号又不是闭括号的任意字符(第(3)点),也可以是另一个完全嵌套的表达式(即第(5)点)。所有的这些,既可以只匹配0次(第(3)点),以便构造最小的完全嵌套括号表达式(),也可以匹配多次来匹配较复杂的表达式。
去掉 /x 选项(即,不再使用多行风格的注释模式),可以简记为:
$regex = qr/\(([^()]+|(??{$regex}))*\)/;
但是切勿在正式产品中使用这一特性,它太诡异,不好把握。建议使用较稳定成熟的Text::Balanced 或 Regexp::Common 模块。
rex注:对于(??{code}),perl官方的提示是:此正则表达式仅作测试使用,可能有更新而不作提示。代码执行时产生的副作用,因版本而异,运行结果或有不同,取决于正则引擎的后期优化。
最后提醒大家,在Perl 5.10中已经可以使用递归捕获缓存来替代懒惰代码子表达式了,运行结果相同。
下面是匹配0n1n的递归捕获缓存语法(?N)的实现:
my $rx = qr/(0(?1)*1)/;
(?1)*的含义是“匹配第一组0或多次”,这里的第一组是指整个正则表达式。
请自行动手,重写平衡括号的正则表达式,当作练习。
祝玩得开心!
数字转美元程序
Feb 15th
本程序将数字转换为英文的美元数,如: 输入
./num2eng.pl 1,100,834.10
则输出:
Total: Say US Dollars One Million One Hundred Thundsand Eight Hundred and Thirty-Four and Ten Cents Only.
注意事项:
- 整数部分可以使用半角的逗号、空格、单引号、下划线、中划线分隔。
- 分隔符的位置可以任意(每3位可,每4位也可),可以任意组合(可以混合使用上述的分隔符)。
- 如果使用单引号,请注意在最外边加上双引号以免转义。
饭否消息析取之regex vs xml
Oct 8th
页内导航:
批量导出饭否程序的方法很多,但是基本思路都是先将该网页保存到本地,然后将有用的饭否消息析取出来。本文不讨论如何下载饭否网页了(使用迅雷、wget、curl等),重点讨论对于下载到本地的网页,如何将有用的饭否消息析取出来。
饭否私信格式分析
May 31st
URL
饭否私信分为两种,一种是我收到的私信,一种是我发出的私信。
- 我收到的私信:http://fanfou.com/privatemsg/p.(1-N)
- 我发出的私信:http://fanfou.com/privatemsg/sent/p.(1-N)
上面的地址中不含饭否ID;需要cookie验证。
结束标志
通过cookie验证后,可以使用数字获得对应页码的私信内容。什么时候是结束呢?假如您的收件箱有1000条私信,每页显示20条,那么当你您输入http://fanfou.com/privatemsg/p.51时,就得不到任何有效的内容了。作为程序,它是寻找如下标志:
<ol class="wa">\s*</ol>
好友列表
在页面代码中,每页都有一个“向XXX发送私信”的combox列表,条目以昵称+ID组成。如果你的好友很多的话(500+),每条好友(昵称+ID)需要20字节(估算)的话,20*500=10K,大约需要多抓取10K的字节量。
收件箱饭否私信的结构
收到的私信分为两种,一种是有回复信息的(回复原文:…),一种是没有回复的。先从简单的入手,看没有回复的。
所有的私信都在<ol class=”wa”>…</ol>之内,以<li></li>分隔
例如下面这一条,就是一则很规范的私信(与发件人相关的信息都使以正则式表示):
<li> <a href="/[^"]+" title="[^"]+" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">没法比较啊,你得说个具体的值,比如100条以下的算少,1000条以上的算多……</span> <span class="stamp time" title="2008-05-30 17:25">约 15 小时前</span> <span class="op"> <a href="/privatemsg.reply/583520">回复</a> <a href="/privatemsg.del/583520" class="post_act">删除</a></span> </li>
其中,需要记录的信息有:
- 发件人名字;
- 发件人ID;
- 私信内容;
- 时间;
- 私信ID;(便于作删除、回复处理)。
根据以上需求,将上面的私信代码作处理:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">([^<]+)</span> <span class="stamp time" title="([^"]+)">[^<]+</span> <span class="op"> <a href="/privatemsg.reply/(\d+)">回复</a> <a href="/privatemsg.del/\d+" class="post_act">删除</a> </span> </li>
从而得到:
$1=fanfou ID; $2=fanfou name; $3=private msg; $4=msg time; $5=msg ID;
再看一下包含“回复原文”的私信的结构(部分内容已作正则处理):
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">([^<]+)</span> <span class="stamp time" title="([^"]+)">[^<]+</span> <span class="op"> <a href="/privatemsg.reply/(\d+)">回复</a> <a href="/privatemsg.del/\d+" class="post_act">删除</a> </span> <p class="pm-parent">回复原文: 有兴趣就有动力</p> </li>
与前者相比,只是多了<p class=”pm-parent”>.*?</p>这一段。这是不足为虑的。只要整体加上?这个强有力的正则符号,就能与上面的代码片段归纳到一起。两者结合合的代码如下:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 来自<a href="[^"]+">[^"]+</a>: <span class="content">([^<]+)</span> <span class="stamp time" title="([^"]+)">[^<]+</span> <span class="op"> <a href="/privatemsg.reply/(\d+)">回复</a> <a href="/privatemsg.del/\d+" class="post_act">删除</a></span> (?:<p class="pm-parent">([^<]+)</p>)? </li>
得到的变量为:
$1=fanfou ID; $2=fanfou name; $3=private msg; $4=msg time; $5=msg ID; $6=parent msg;#回复原文。
发件箱饭否私信的结构
抄代码:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 发给<a href="[^"]+">[^"]+</a>: <span class="content">海内的像片是真的。</span> <span class="stamp time" title="2008-05-28 20:24">2008-05-28 20:24</span> <span class="op"><a href="/privatemsg.del/576827" class="post_act">删除</a></span> </li>
这与“我收到的私信”的结构完全一致,只是将原来的“来自”改为“发给”而已。
不出意外,带有“回复原文”的“我收到的私信”的结构是这样的:
<li> <a href="/([^"]+)" title="([^"]+)" class="avatar"><img src="[^"]+" alt="[^"]+" /></a> 发给<a href="[^"]+">[^"]+</a>: <span class="content">在自述部分显示的那个网上。</span> <span class="stamp time" title="2008-05-29 17:00">2008-05-29 17:00</span> <span class="op"><a href="/privatemsg.del/579918" class="post_act">删除</a></span> <p class="pm-parent">回复原文: 我也要试一试。</p> </li>
我们从饭否私信代码上得到的信息就这些。遗憾的是,饭否私信中,关于“回复原文”是以明文内容形式出现,而不是以原私信ID的形式出现。后期处理时通过搜索功能解决此问题并非不能,只是如果饭否官方能够再将此功能完善的话,会省整理者不少力气。
饭否的私信源码分析完毕。至于如何读写cookie,如何写代码,如何以数据库的形式来管理下载的数据,是第二阶段的事情了。待我一一实现。
windows下的正则式工具介绍之一:RegexBuddy
Apr 30th
俗话说,工欲善其事,必先利其器。关于windows下的正则表达式工具,这里推荐的是:RegexBuddy和PowerGREP。在linux下,也有好用的正则表达式工具,例如grep的兄弟们,只不过是都是基于命令行的。而这两款windows下的小工具,其突出特点是可视化,允许尝试和预览,极大地方便了使用者。
RegexBuddy:网址是http://www.regexbuddy.com。在编写正则式时,它提供可视化的支持、提示、调试方面的便利;在使用正则表达式时,它无私地将正则式转换为多种语言的字串,还提供了代码输出功能。正则式助手,该称号名副其实。
基本界面
正则式的基本功能无外乎搜索和替换。在本文中,我们使用匹配Email的正则式,代码如下:
\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b
此时,RegexBuddy的界面如图:
在点击“Explain Token”时,能对当前的正则式片断的作用作出详细解释,例如当你把光标移动到\b上再点击Explain Token,就会激活帮助文档,自动定位到Word Boundaries这一段。
如果想对刚才编写的这条正则式进行测试和验证,可以点击“Test”进行测试。这时,在下边的文本框输入所需要匹配测试的文字,例如dog@animals.com,匹配结果就以黄色背景色标出。在本例中,你或许没有得到正确的匹配,呵呵,那是正常的。为什么?答案见文章结尾。
拷贝粘贴
RegexBuddy能把正则式以多种字符串格式拷贝出来。还是刚才那条正则式,根据需要,它可以被拷贝为:
'\\b[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b' "\\b[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b" '/\\b[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b/i'
具体的选项在copy菜单里,如下图所示。你不必为单双引号、正反斜线操心了。
在paste菜单项中也有类似应用,不赘述。
正则式->代码
如果你想把刚才编写好的正则式应用在程序中,这里还有一个选项:Use,界面见下图:
在上面的match和replace之间点击,其代码也相应自动调整;当选取不同的language时,代码也会相应调整。它支持的语言格式为:
- C#
- Delphi(NET/Win32)
- Java/JavaScript/ECMAScript
- PCRE
- PHP
- Perl
- Python
- RealBasic
- Ruby
- VB
另外,它还有function选项,分别用以实现下述功能:
- If/else branch whethe the regex matches (part of) a string. If/else验证正则式是否匹配字串(的一部分)。 最常用的功能。
- If/else branch whethe the regex matches a string entirely. If/else验证正则式是否匹配整条字串。
- Get the part of a string matched by the regex. 取得字串中与正则式匹配的部分。Get the part of a string matched by a capturing group. 取得字串中所匹配的捕获组。这一条我也是刚刚知道,很有用哟。
- Get an array of all regex matches in a string.将字串中所有的匹配保存到数组中。
- Iterate over all matches in a string。列出字串中所有的匹配项。(例如,在使用正则式’\w’来匹配字串’abc’时,本function列出的内容为’a',’b',’c’.)。单词iterate的含义是重复。
- Comment with RegexBuddy’s regex tree. RegexBuddy的正则树的注释。
文本分割split
如果需要处理的文本是以某种分隔符隔开的,而该种分隔符恰好又能使用正则式描述,(例如html标签),此时regexbuddy的split功能就可以大显身手了。我随便打开了一个饭否网页,对其源代码中的消息部分(<div id=“stream”>与</div>之内)的文本进行了处理,使用如下正则式删除了所有的尖括号内容,只留下普通文本。
使用的正则式为:
<[^>]+>
软件界面以及运行结果请见下图。
结尾:
关于本文开头提出的小问题,细心的你或许一下子就能看出答案了!见下图:
只要选中Case insensitive选项中OK啦!如果你没有找到,或许是因为该软件是英文的,一时间您没有注意到该选项;或者您对正则式还不太熟悉。
软件下载
上文已经提到,其官网为www.regexbuddy.com,可以去下载其最新版试用。该软件为商业软件。
- 如果你偶然路过,尝新而已,那只需下载试用版即可;
- 如果你觉得好用、准备常用、手有余钱、非正版不用,不妨花美金购买;
- 如果你喜欢它,同时你认为优秀的网络资源是应该和朋友免费分享的,从而想获得该软件的全功能免费版,好吧,我也成全你,请在本文后留言(附邮箱),我会把这个小东西的链接发给你(最新版为3.1.1,我手头的全功能版为3.1.0,也足够用了)。
下篇文章将要介绍另一款regex工具:PowerGREP,敬请期待。
本文为rex.zhasm原创,原文地址在http://iregex.org/blog/y2008/m04/d30/regexbuddyregexbuddy.html,可以在遵循CC协议的条件下转载。
————————————-
2008.12.26 更新:
本文已经关闭评论,即将发布3.2.0完全版。不会晚于2008.12.28。敬请期待。
2008.12.28 更新:
请移步至此下载RegexBuddy 3.2.0版。
[老贴整理]如何使用正则式从英文句子里提取词根
Apr 25th
以前在chinaunix回答过这样一个问题,用到了正则表达式(而且我认为正则式解决此类问题是最合适的。)
学英语的一些例句,每句都有若干词根相同的词,例如 She swears to wear the pearls that appear to be pears. 但是每句的词根都未必相同;我希望把这些包含词根的词都标记出来,请问如何写?
这里说的词根不是原本词根的定义,只是一组字母序列,比如
9. The dust in the industrial zone frustrated the industrious man.
词根是dust或ust
10. The just budget judge just justifies the adjustment of justice.
词根是dust
11. I used to abuse the unusual usage, but now I’m not used to doing so.
词根是use,有变形
12. The lace placed in the palace is replaced first, and displaced later.
词根是lace
13. I paced in the peaceful spacecraft.
词根是pace
14. Sir, your bird stirred my girlfriend’s birthday party.
词根是ir
如果您对此问题感兴趣,请独立思考后再继续阅读本站提供的解决方法。










Comments