「REGULAR EXPRESSION」- Perl,正则表达式,扩展示例

问题描述

本章节的主要目的是通过一些示例,对正则表达式做进一步的介绍。我们会跳出egrep命令,使用其他工具来介绍正则表达式。

上一章节我们围绕了egrep命令对正则表达式进行了介绍。介绍了egrep中的正则元字符及使用方法,主要侧重的是使用egrep配合正则表达式进行文本查找。期间还提及了许多支持正则表达式的其他语言,比如PHP、JAVA、VB.NET、Perl。这些语言的对正则支持和操纵能力都远远强于egrep。本章我们将使用Perl语言进行示例的讲解。因为Perl语言对正则表达式的支持很完整,而且非常易于使用,可以让我们把精力都放在正则表达式本身上,而不需要太多的去关注语言的细节。

本章节中的示例涉及一些常见问题的处理。我们用日常问题的处理来演示正则表达式。

解决方案

本章中我们用Perl语言进行正则表达式的讲解。这里Perl语言只是讲解正则表达式的工具,并不会过多涉及Perl语言本身的内容,但是依旧需要对Perl语言进行简单额介绍。

Perl语言简单入门

Perl关于文本处理和正则表达式的许多概念来自于来个专业化的文本处理语言工具awk和sed。如果你用过这两个工具,你会发现Perl关于正则的概念及用法与sed和awk中的用法及其类似。Perl可以在很多的平台中应用,比如Windows、Linux、VMS、Unix、OS/2。Perl的文本处理能力极强,在文本处理中Perl是最常用的工具。接下来我们先简单的介绍以下Perl语言的语法。

先看一个简单的Perl程序示例:

$f = 30;                	# 变量赋值
$r = ($f * 2 / 5) + 34;	# 进行计算
print "\$r is $r.\n";   	# 打印结果

上面这段程序的运行结果是:「$r is 46.」。变量一般以美元符号($)开始。井号(#)开始的到行尾表示注释。变量能够出现在双引号包含的字符串中,如$r,这一点与SHELL、PHP等语言类似,但是JAVA中是不能这么做的。

Perl中也有类似与其他语言中的控制结构:

$f = 30;                		# 变量赋值
while ($f < 45)
{
	$r = ($f * 2 / 5) + 34;	# 进行计算
	print "\$r is $r.\n";   	# 打印结果
	$f = $f + 5;
}

只要$f < 45为真,循环控制while就会一直执行。将上面的程序写入文件demo,然后从命令行运行它就会产生如下输出:

# perl -w demo

$r is 46.

$r is 48.

$r is 50.

-w选项只是告诉perl命令对demo文件进行检查,如果有不正确或者由可能出现问题的地方进行警告,比如变量未定义、变量未赋值等等。

使用正则表达式匹配文本

Perl可以以多种方式使用正则表达式,最简单的就是进行文本匹配,检查某个文本能否与某个正则表达式匹配。这也是本部分的主题:正则表达式的文本`匹配功能

匹配文本中的正整数数字

下面的Perl代码用于检查字符串是否全部由数字组成:

if($str =~ m/^[0-9]+$/){
print “只有数字”;
} else {
print “不全是数字”;
}

我们先讲解一下这段代码:正则表达式是^[0-9]+$,两边的m/…/告诉Perl对该正则表达式进行什么操作,m表示进行”正则表达式匹配”,斜线标注出正则表达式的边界。=~用来连接正则表达式与被搜索的字符串,本例中的被搜索的字符串是$str。如果$str与正则表达式匹配,则$str =~ m/^[0-9]+$/的返回值为true,否则为false。配合if语句,如果为true则打印“只有数字”,否则打印“不全是数字”

我们将上面的Perl代码封装一下,做成一个检查用户是否输入了数字的脚本工具isNum,isNum中的代码为:

print “请输入一个数字:”;

$str = <STDIN>;

chomp($str);

if($str =~ m/^[0-9]+$/){
print “只有数字\n”;
} else {
print “不全是数字\n”;
}

从命令行中执行这个isNum脚本:

# perl -w isNum

请输入一个数字:12

只有数字

# perl -w isNum

请输入一个数字:12A

不全是数字

# perl -w isNum

请输入一个数字:12.1

不全是数字

匹配文本中的数字

上面的示例只能够匹配正整数,但是不能匹配所有的整数的小数。接下来我们扩展一下上面的那个示例,让我们的isNum脚本能够匹配整数和小数。

要能够匹配小数和整数,首先要容许输入的数字中有正(+)负(-)号或者没有,我们可以使用-?,但是没有办法匹配带有正好的情况,所以我们使用[-+]?来处理开头的正负号。

除此之外,我们还要容许出现小数部分。小数部分也可能不出现。我们使用(\.[0-9]*)?来匹配小数部分。\.[0-9]*会匹配小数部分,但是小数部分不见得是必须的,所以我们使用()?来包裹表达式。

综合起来得到我们最终的表达式:^[0-9]+(\.[0-9]*)?$

关于修饰符

在egrep中,我们查找的时候文本时,有时希望查找匹配的时候忽略字母大小写,幸好egrep提供了-i选项。-i选项可以使得egrep在查找时可以忽略大小写。那Perl中该如何忽略大小写呢?

修饰符。使用修饰符号。在正则表达式中带修饰符的Perl程序看起来会是下面的样子:

$str =~ m/^[a-z]+$/i

修饰符在最后一个反斜线后面,可以指定多个修饰符。上面的正则表达式中使用了修饰符号i,该正则表达式在$str中查找字母时会忽略大小写。

使用正则表达式替换文本

到目前为止我们看到的例子都是使用正则表达式进行文本匹配,接下来我们来看一下Perl语言和其他语言中提供的另一个正则表达式特性:替换。

前面,我们使用类似于$var =~ /regexp/的结构来使用正则表达式来匹配文本,并返回匹配是否成功的布尔值。与之类似的结构$var =~ s/regexp/replacement/更进一步,如果在$var中发现匹配内容,则会将匹配的内容替换成replacement。regexp的为正则表达式与之前的m/…/中的用法相同。而replacement中可以使用反向引用来引用之前匹配的具体文本,这点在讲egrep的时候提到过。

所以,使用$var =~ s/regexp/replacement/时,会替换$var变量的内容。

文本内容替换

接下来我们看一个文本替换的示例。如果变量$var的内容为Ni Jason,运行下面程序时:

$var =~ s/Ni/Niblack/;

变量$var的内容就会变层NiBlack Jsaon。如果在运行一次就会变成NiBlackBlack Json。要避免这种状况,我们可以使用单词分界的元字符。在egrep中我们提到过单词分界的元字符\<、\>。Perl中也有类似的单词分界元字符,但是Perl中的单词分界元字符是\b。所以,如果要避免刚才的问题,在Perl中,我们可以将正则表达式进行如下修改:

$var =~ s/\bNi\b/NiBlack/;

与m/…/一样,s/…/…/中也可以使用修饰符。比如,下面的正则表达式使用了修饰符i,即查找的时候忽略大小写:

$var =~ s/\bNi\b/NiBlack/i;

该正则表达式会将$var中的NI、Ni、ni、nI都替换成NiBlack。当然由与单词分界元字符\b的存在,NI、Ni、ni、nI都必须是独立的单词,即两边不能为字母,只能是字母以外的字符。

数字格式化

数字显示时,有时候需要格式化。比如:33.12000002,这种数字很可能是因为计算机对浮点数的处理导致的。我们可以用用正则表达式对其进行格式化为33.12。这时候,我们可以使用“反向引用”,匹配出数字中的整数部分和小数部分。匹配整数部分和小数部分本质上是一致的,都可以使用\d+,但是小数部分我们指向保留两位所以可以使用\d\d来表示小数部分中的两位数字。所以用于格式化数字的Perl代码如下:

$var = s/(\d+).(\d\d)\d*/\1.\2/;

这里使用了反向引用,其中\1代表第一个括号匹配的内容,\2代表第二个括号匹配的内容。

正则的环视功能

正则表达式中有一个“环视(lookaround)”功能。环视功能用于匹配文本中的位置。这一点与\b、^、$元字符类似,不会占用任何字符,只匹配指定位置。

环视分为两种“顺序环视”与“逆序环视”。

顺序环视从左到右查看文本,如果能够匹配就返回匹配成功信息。它的语法结构为(?=…)。比如(?=\d),如果当前位置的右边为数字,则匹配成功。

逆序环视从右到左查看文本,如果能够匹配就返回匹配成功信息。它的语法结构为(?<=…)。比如(?<=\d),如果当前位置的左边为数字,则匹配成功。

首先注意一点:环视不会占用任何字符它表示的是一个位置。可能是夹在两个临近字符之间的那个位置。要注意的第二点:不管顺序环视还是逆序环视,正则表达式执行时都是从左到右开始检查文本的,上面所说的“从左到右查看文本”与从右到左查看文本“指的是当前位置的右边和当前位置的左边。

让我们来看一个环视的例子。我们常见的英文文档中,数字通常是三位一组并使用逗号分割的,比如23,456,789。接下来,我们要做的就是使用环视功能将23456789格式化为23,456,789。这里就用到的环视功能。首先,数字是三个为一组的,我们可以用正则表达式(\d\d\d)+$来表示,而逗号的左边只要是数字就可以了,这个可以使用正则表达式\d来表示。所以最后Perl程序中的正则表达式的结构是下面的样子:

$str =~ s/(?<=\d)(?=(\d\d\d)+$)/,/g;

上面的Perl程序执行后,会将23456789格式化为23,456,789。结尾的$符号是必须的,如果没有结尾的$符号,格式化后的结果就会变成2,3,4,5,6,789。

总结

在Perl语言中使用正则表达式进行匹配,检查某个文本是否与某个正则表达式匹配。

正则表达式修饰符的使用。修饰符跟在正则表达式边界的后面(最后一个斜线的后面)。指定了修饰父i之后,在进行正则匹配时忽略大小写。

使用正则表达式s/…/…/进行文本替换。将匹配的内容替换成指定的文本。

使用正则表达式的环视功能来匹配一个位置,然后在该位置插入文本。环视表达式能够匹配一个位置,但不会占用任何字符。

正则表达式反向引用。通过\1、\2、\3…的形式来代表前面匹配的内容,可以用来匹配重复文本,或者将替换内容设置为之前括号中匹配的内容。