Perl one line command – 常用正则

Perl one line command - 常用正则

本章说明一些常用的正则表达式, 比如匹配 IP 地址, HTTP 头信息, email 地址等.

1. 匹配 IPv4 地址

IP 地址格式 xxx.xxx.xxx.xxx, 使用 \d 来匹配数字,通用的做法如下:

/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/

{1,3}表示匹配最少一个, 最多3个数字,这个表达式没有检查地址的有效性(不能大于255),所以也能匹配出无效的地址,但是对于有效的地址都能匹配出来; 我们可以发现前三部分是一样的, 可以改成:

/^(\d{1,3}\.){3}\d{1,3}$/

{3}表示匹配3次. 如果要检查地址的有效性, IPv4的范围是 0.0.0.0 ~ 255.255.255.255, 每字节可以是 1,2,3 位整数, 如果是 1 位的时候可以使用 [0-9] 来匹配 0 ~ 9, 如果是 2 位可以使用[0-9][0-9] 来匹配 10~99, 如果是 3 位 , 则需要匹配 100 ~ 255 之间的数, 可以使用 1[0-9][0-9] 匹配 100 ~ 199 之间的数, 2[0-4][0-9] 匹配 200 ~ 249 之间的数, 25[0-5] 匹配 250 ~ 255 之间的数.可以使用 | 或操作将这些正则连接起来, 整个表达式如下:

/^([0-9]|[0-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$/

所以整体上匹配一个有效的 IP 地址的代码如下:

$ip_re = qr/[0-9]|[0-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]/;

if ( $ip =~ /^$ip_re\.$ip_re\.$ip_re\.$ip_re$/ ) {
   print "$ip\n";
}

qr 表示引用正则相关的表达式: Regexp-like quote.

2. 匹配邮件地址

一个正常邮件的的形式类似 1234mnop@qq.com, 可以使用以下表达式匹配:

/\S+@\S+\.\S+/

\S 排除了出现空白字符的可能, 既匹配了 @ 符号, 也匹配了 . 符号, 至少看起来满足这个条件的都是正常的邮件, 也可以使用 Email::Valid 模块进行检查, 不过需要额外安装:

perl -MEmail::Valid -ne 'print Email::Valid->address("1234mnop@qq.com") ? "valid email" : "invalid email"' file

? : 为 3 元操作符, address 为真则打印 valid email, 否则打印 invalid email.

3. 检查数字

如何检查数字的有效性, 先来看看有效的数字有哪些:

23                          # 整数
23.1                        # 小数
+23, -23, +23.1, -23.1      # 正负数
1,000                       # 整数

如果再算上复数, 十六进制, 八进制等就更复杂了, 如果是简单的匹配:

/^\d+$/               # 匹配多个数字
/^[+-]?\d+$/          # 正负数, ? 表示可选, 匹配 0 个或 1 个
/^[+-]?\d+\.?\d*$/    # 小数

上面的表达式并没有匹配类似 1,234,456 或 .3 的数字. 可以使用 Regexp::Common 模块来实现匹配:

perl -MRegexp::Common -ne 'print if /$RE{num}{real}/' file

如果要匹配十六进制和八进制, 可以使用以下正则:

/^0x[0-9a-f]+/i
/^0[0-7]+/

i 表示忽略大小写.

4. 检查出现两次的单词

/(words).*\1/

() 用来捕获里面的内容, 并赋值到 1 里, \1 是对 1 的解引用, 表示捕获到的内容. 整个表达式匹配 words.*words

5. 整数加1

$sth =~ s/(\d+)/$1+1/ge

g 为全局匹配, e 表示可以执行代码, ()捕获的内容放到 $1 中, 最有使用 $1+1 替换匹配的整数.

6. 匹配 HTTP 头信息 用 curl 来请求 weibo 的信息, 如下

# curl -I http://www.weibo.com/
HTTP/1.1 302 Moved Temporarily
Server: WeiBo
Date: Wed, 04 Mar 2015 06:26:32 GMT
Content-Type: text/html
Connection: close
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified: Wed, 04 Mar 2015 06:26:32 GMT
......

匹配各个字段都比较容易, 将捕获的内容放到特殊变量 $1 中, 比如:

date: Wed, 04 Mar 2015 06:29:33 GMT

7. 匹配可打印的 ascii 码字符

http://zh.wikipedia.org/wiki/ASCII 详见维基百科, 除去控制字符, 剩下的为可显示字符, 可以看到可显示字符的范围是 空格 到 符号 ~, 对应16进制的 0x20 ~ 0x7e, 了解这些之后, 就可以明白下面表达式的意思:

/[ -~]/

表示匹配空格到符号 ~ 之间的字符, 如果不匹配可打印字符, 使用 ^ 反向即可:

/[^ -~]/

8. 替换标签信息

如果要将 替换为 <p> 或 </p>, 可以使用以下表达式:

$str =~ s#<(/)?b>#<$1p>#g

(/)? 表示匹配并捕获 0 或 1 次, 并赋值给 $1 变量, g表示全局替换.

9. 提取匹配的内容

如下表达式:

@match = $str =~ /regex/g;
@match = ($str =~ /regex/g);

$str =~ /regex/g 匹配所有相关的内容, 返回一个列表, 可以通过数组保存匹配到的内容, 如下所示匹配所有的整数:

# perl  -le '$, = " "; $str = "hell 253, yes 50"; @match = ($str =~ /\d+/g); print @match'
253 50

也可以匹配键值对, 比如字符串”key1=v1; key2=v2,v21; key3=v3,vs3”:

# echo "key1=v1; key2=v2,v21; key3=v3,vs3" | perl -lne '@vals = $_ =~ /[^=]+=([^;]+)/g; print "@vals"'
v1 v2,v21 v3,vs3

表达式首先匹配 [^=]+ 即不是 = 的字符串, 再匹配 = , 再匹配 [^;]+ 即非 ; 的字符串, 再将结果存到数组中.

文章参考: PERL ONE-LINERS Copyrught @ 2014 by Peteris Krumins ISBN-10: 1-59327-520-X