# 正则表达式 ## 112.匹配貌似邮件的正则表达式 /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ 这个正则表达式不能保证匹配都是合法的ip地址,匹配成功只不过貌似ip地址的字串。比如,它可以匹配一个合法的ip地址81.198.240.140,同时也能匹配一个非法ip地址比如923.844.1.999。 下面解释他是如何工作的。正则开始的^符号是一个锚字符,用来匹配一个字串的开始。接着\d{1,3}匹配一个,两个或者三个连续数字。“\.”匹配一个点。最后的$也是一个锚字符匹配字段的结尾。使用^和$锚字符非常有必要,否则的话诸如“foo213.3.1.2bar”一样的字串也会被匹配上。 这个正则可以简单的把开始部分\d{1,3}\.重复三次的到同样功能的表达式: /^(\d{1,3}\.){3}\d{1,3}$/ ## 113.测试数字是否在0-255的范围内 /^([0-9]|[0-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$/ 让我来看这个正则是如何工作的。此范围内的数可以为一个,两个或者三个数字。如果是一个数字,我们可以让它为任何[0-9]。如果是两个数字,我们也允许它为任何[0-9][0-9]的组合。然而如果它是三个数字的,则他必须为100多或者200多的数。如果为100多的,可以用1[0-9][0-9]匹配它。如果是200多的数字,如果是小于250的,可以用2[0-4][0-9]匹配。或者250-255的,用25[0-5]匹配。 ## 114.匹配IP地址 my $ip_part = qr|([0-9]|[0-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|; if ($ip =~ /^($ip_part\.){3}$ip_part$/) { say "valid ip"; } 本正则组合了前面的两个。它用my $ip_part=qr/…/操作把正则表达式组合到一起,并且存到$ip_part变量中。接着用这个变量去匹配IP地址的四个部分。 ## 115.检查字符串是否貌似email地址。 /.+@.+\..+/ 这个正则确保字符串看起来像一个邮件地址。注意到这说的是"看起来像"。它不能保证确实是一个邮件地址。他的匹配过程是:首先匹配一些字符后跟个@符号,接着匹配任意字符然后到发现一个点,接着匹配更多个字符。如果这个匹配成功,那么这个字符串至少看起来就像个个邮件地址,他有@和.。 例如,admin@ijz.me可以匹配,但是admin@ijzme不会匹配,因为他匹配不到点\.,这是必须的。 其实更可靠检测合法邮件地址的方法是是用Email::Valid 模块: use Email::Valid; print (Email::Valid->address('john@example.com') ? 'valid email' : 'invalid email'); ## 116.检测字符串为十进制数。 检测字串是否是数字非常难。我基于正则并且在Perl Cookbook对其做了解释。 /^\d+$/ 这个正则匹配一个或者更多的数字\d为了开始符^和结尾符$。但是它不能匹配诸如+3和-3这样的数字。让我们调整一下,匹配他们: /^[+-]?\d+$/ 此处[+-]?意思是在数字前匹配一个可选的正号或者负号。这个可以匹配+3或者-3,但是还不能匹配-0.3。让我们加上它: /^[+-]?\d+\.?\d*$/ 我们在前一个正则的基础上加了\.?\d*,这匹配了一个可选的.在0或者其他数字后面。现在这个正则可以匹配诸如-0.3或者0.3的数字了。 更好的方式是是使用Regexp::Common模块,它提供了各种非常有用的正则表达式。比如,匹配一个整数你可以用$RE{num}{int}。 那么怎么匹配一个正16进制数字呢?见下式: /^0x[0-9a-f]+$/i 这可以匹配十六进制前缀0x,紧跟着数字。模式结尾的/i标志确保匹配是不区分大小写的。例如0x57af匹配,0X5Fa也匹配,但是97匹配不了,由于他是一个十进制数。 当然最后是用模块$RE{num}{hex},它会支持负数,小数和逗点的数字组。 那么八进制呢?见下: /^0[0-7]+$/ 8进制数前缀是0,其后紧跟八进制数字0-7。例如,013匹配,但是09不会匹配,因为9不是一个合法的八进制数。 更好的方式是使用$RE{num}{oct},好处同上。 最后2进制: /^[01]+$/ 二进制只包含0和1.例如,0101101匹配,但是210101不能,因为2不是合法的二进制数字。 更好的方式是使用$RE{num}{bin}。 ## 117.检测一个单词在字符串总出现了两次。 /(word).*\1/ 这个正则匹配了word紧跟着任意字符,随后是同样的word。此处(word)捕捉了word 分组1,并用\1引用了分组1的内容,因此这个和写作/(word).*word/的模式一样。例如,“silly things are silly”会匹配/(silly).*\1/,但是“silly things are boring”不会匹配,因为后面这个字串中silly没有重复。 ## 118.给字串中的所有数字加1. $str =~ s/(\d+)/$1+1/ge 此处我们使用替换操作符s///。他匹配所有的数字(\d+),把他们捕捉到组1,接着把他们的值替换其值加1后的值。g标志确保它会知道字串中的所有的数字,e标志使得$1+1作为一个Perl表达式执行。 例如,“this 1234 is awesome 444”会被替换为“this 1235 is awesome 445”。 ## 119. 提取HTTP头中用户客户端字串 /^User-Agent: (.+)$/ HTTP头以键值对的格式表示的。操作这些字串很简单,你只要叫正则机保存值部分在$1组变量。 例如,如果HTTP头包含, Host: localhost:8000 Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_0_0; en-US) Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 . 那么正则表示式将会提取Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_0_0; en-US)字串 ## 120.匹配可打印字符。 /[ -~]/ 这个一个非常巧妙的表达式。为了理解他,看下ascii的说明。你可以发现空格符起始于0x20,~符号是0x7e。所有的空格和~之间的字符的都是可以打印字符。这个正则表达式恰好匹配了这些。[ -~]定义了一个从空格到~的字符范围。这一直是我最喜欢的正则表达式。 /[^ -~]/ 这个匹配恰好和[ -~]相反。 ## 121.匹配两个HTML签之间的文本 m|([^<]*)| 这个正则表达式匹配了所有... 签之间的内容。此处技巧是([^<]*),它匹配会匹配尽可能多的字符知道发现一个<字符,他会开始另一个标签。 另外你也可以写成: m|(.*?)| 但是这个有点不一样了。例如,如果HTML内容是 hello 则第一个正则不能匹配,但是第二个正则表达会匹配到hello,由于(.*?)匹配尽可能少的内容知道发现。这恰好是hello。 但是我们一般不建议用正则表达式去匹配和处理HTML。使用诸如HTML::TreeBuilder 模块去做这项工作,是更明智的选择。 ## 122.替换所有的签为 $html =~ s|<(/)?b>|<$1strong>|g 此处我假设HTML内容保存在变量$html中。接着<(/)?b>匹配了起始和结束的签,捕捉可选的可选签在组$1,接着用 或者 替换匹配的签,这取决于匹配的是一个起始或者结束tag签。 ## 123.提取正则表达式中所有匹配的部分。 my @matches = $text =~ /regex/g; 此处正则表达式在list上下文中,使得他返回所有匹配。匹配的内容被放进@matches数组中。 例如,下面的正则表达式提取了一个字符串中所有的数字。 my $t = "10 hello 25 moo 31 foo"; my @nums = $text =~ /\d+/g; @nums 现在包含了(10, 25, 30)。