正则表达式:regular expression (regex)‘,用以描述具有某种特征模式的字符串
1. 序列
perl的正则表达式要么匹配某个字符串,要么不匹配,没有介于中间的部分匹配
perl会寻找从左起尽可能长的符合条件的匹配字符串
最简单的匹配模式是序列,要找什么字符直接写到匹配模式中
$_ = "yabba dabba doo";
if(/abba/){
print "It matched!\n";
}
if语句的条件表达式中,2个斜线就是匹配操作符,默认用它匹配$_中的字符串,斜线之间的部分就是要匹配的模式
匹配过程是从最左侧开始尝试匹配的,找到之后不会继续看后面的dabba是否也匹配,这与全局匹配不一样
在perl模式中,空白字符值得关注,任何在模式中写明要匹配的空白字符,意思就是要寻找匹配$_中完全相同的空白字符
$_ = "yabba dabba doo";
if(/ab ba/){ # 不能匹配
print "It matched!\n";
}
$_ = "yabba dabba doo";
if(/ba da/){ # 能匹配
print "It matched!\n";
}
写在匹配操作符中的模式文本相当于写在双引号内,比如\t和\n这样的特殊序列表示的就是制表符和换行符
制表符的N中表示方式
/coke\tsprite/ # \t表示制表符
/coke\N{CHARACTER TABULATION}sprite/ # \N{charname}
/coke\011sprite/ # 字符的八进制码
/coke\x09sprite/ # 字符的十六进制码
/coke\x{9}sprite/ # 字符的十六进制码
/coke${tab}sprite/ # 标量变量
perl在运行时先完成变量内插,取得表示模式的字符串后,再编译这个正则表达式,编译时如果发现这个字符串非法,会收到错误信息
$pattern = "(";
if(/$pattern/){
print "It matched!\n"; # 编译错误,因为括号在perl中有特殊意义
}
用1个空的序列去匹配,配啥都唔那个匹配上
$_ = "yabba dabba doo";
if(//){
print "It matched!\n";
}
根据“从最左侧起最长匹配”的原则,在字符串开始的位置总是可以匹配一个没有长度的序列
2. 动手实践不同模式
模式字符串可以用变量内插,改成从命令行接受参数,作为要要测试的模式是不是会更好?
while (<STDIN>){
chomp;
if(/$ARGV[0]/){ # 直接使用参数会有安全隐患
print "\tMatches\n";
}else{
print "\tDoesn't match\n";
}
}
因为输入的参数可以是任何东西,而perl拿到之后直接当正则表达式进行编译运算,所以从命令行接受参数会有安全隐患
% perl try_a_pattern "fred"
This will match fred
Matches
But not Barney
Doesn't match
现在可以不改程序代码就直接测试不同模式了
% perl try_a_pattern "barney"
This will match fred (not)
Doesn't match
But it will match barney
Matches
3. 通配符
点号. 能匹配除换行符外的任意单个字符
perl认为一般意义上我们要匹配的是换行符前的文本,那才是输入的内容,而换行符不过是行与行间的分隔,是格式的一部分
$_ = "yabba dabba doo!";
if(/doo./){ # 能匹配
print "It matched!\n";
}
$_ = "yabba dabba doo\n";
if(/doo./){ # 不能匹配
print "It matched!\n";
}
如果像匹配实际的点号字符,需要先用反斜线转义
$_ = "yabba dabba doo.";
if (/doo\./){ # 能匹配
print "It matched!\n";
}
perl5.12增加了另一个“能匹配除换行符外任意单个字符”的写法 \N
4. 量词
可以用量词来指定匹配项的重复次数,量词也是元字符,量词把它之前的字符作为要匹配的目标,然后定义它该重复匹配的次数
匹配次数 | 元字符 | 一般化写法 |
---|---|---|
可有可无 | ? | {0,1} |
0或多次 | * | {0,} |
1次以上 | + | {1,} |
至少多少次 | {3,} | |
指定重复次数范围 | {3,5} | |
准确的重复次数 | {3} |
5. 模式分组
用圆括号将模式字符串进行分组,圆括号也是元字符
量词只作用于它前面的那个字符,模式/fred+/匹配的是freddddddddd这样的字符串,因为量词+之前的是字母d。如果要匹配整个fred的重复次数,就需要用括号把fred括起来,写成/(fred)+/,这个时候匹配的是fredfredfred这样的字符串
圆括号还能帮助重复利用匹配上的字符串,可以使用反向引用(back reference),引用圆括号匹配到的字符
由于能取得匹配结果,这种括号围起来的分组叫做捕获分组(capture group)
反向引用:在反斜线后面接上数字,每个数字代表对应的捕获分组,按括号出现的先后次序而定
$_ = "abba";
if(/(.)\1/){ # 匹配连续出现的2个同样的字符,能匹配'bb'
print "It matched same character next to itself!\n";
}
$_ = "yabba dabba doo";
if(/y(....) d\1/){ # 反向引用不需要紧贴在对应捕获分组括号的右边
print "It matched the same after y and d!\n";
}
$_ = "yabba dabba doo";
if(/y(.)(.)\2\1/){ # 可以用多个括号形成不同分组,每个分组对应1个编号,1个编号对应1个反向引用,这个用来匹配像abba这样的回文
print "It matched after the y!\n";
}
$_ = "yabba dabba doo";
if (/y((.)(.)\3\2) d\1/){ # 如何知道某个分组对应的数字编号?不用管嵌套的情况,从左起数左括号出现的次序即可
print "It matched!\n";
}
perl5.10引入了1个新的标记反向引用的写法 \g{N}
,其中g表示分组,N就是原来的数字编号,而\g{N}
也可以简写为\gN
perl会据需创建反向引用,但只保留\1到\9的缓存
$_ = "aa11bb";
if(/(.)\111/){
print "It matched!\n"; # 反向引用是\1还是\11或者是\111呢?计算机需要推测你的推测
}
编号数字可以使用负数,和python差不多