前言
上一篇文章中我们研究了行的起止符、字符组和点号等正则表达式的元字符,有的同学可能注意到,在第一篇学习笔记中提到的
(.*?)
正则表达式的含义还是不明朗,因为
*?
这两个符号到目前为止,我们还没有分析过。
这一篇文章中,我们继续来研究其它的元字符,之后用学到的知识来给
(.*?)
做出解释。
多选分支
我们已经知道字符组是由中括号
[]
括起来的,而多选结构则是由小括号
()
括起来的,从匹配字符的数量来看,字符组相当于多选结构的特殊形式,因为字符组提供了
单个字符的备选匹配,事实上,多选结构也可以完成单个字符的备选匹配,只不过需要在备选的字符之间用
|
进行分割,下面给出
Perl
代码演示:
$value = 'Hello world! Hella world!';while ($value =~ m/Hell(o|a)/){ print "匹配到的字符:$& \n"; $value = $'}
在上述代码中,
(o|a)
就是所谓的多选结构,它可以用字符组
[oa]
代替,对它运行的结果如下:
和字符组不同的是,多选结构可以容纳更多字符的备选,将上面例子中的正则表达式修改为
Hel(lo|la)
,同样可以匹配到一样的结果,我们用
Python
来实现如下:
忽略大小写
在匹配字符串的时候,有时我们是需要忽略大小写的,比如给出的字符串是
Hello world! Hella world!
,若我们所写的正则表达式
hell(o|a)
需要对该字符串进行匹配,就需要用到忽略大小写开关,在
Perl
中是这样写的:
$value = 'Hello world! Hella world!';while ($value =~ m/hel(lo|la)/i){ print "匹配到的字符:$& \n"; $value = $'}
上述代码中的
i
即是忽略大小写的开关,运行代码如下:
在
Python
中,忽略大小写的开关是添加
flags=re.IGNORECASE
,代码如下:
单词分界符
在英文中,单词是以空格进行区分的,这就牵扯到单词的分界符问题,在
Perl
中,单词的分界符是
\b
,将要匹配的字符串放在两个
\b
之间,就意味着必须要对一个单词进行匹配,示例如下:
$value = 'Hello world! Helloo world! Hella world!';while ($value =~ m/\bhel(lo|la)\b/i){ print "匹配到的字符:$& \n"; $value = $'}
在上述代码中,我们添加了一些单词,当正则表达式中不含单词定界符时,它会输出三个结果,但加上定界符后,运行结果只有两个字符串,如下图所示:
在
Python
中的单词定界符也是
\b
,但要注意,当为正则表达式添加
\b
时,该字符串前要添加
r
字母,
r
的意思是保持字符串的原生态,这样转义字符就会保持原来的意义:
量词的应用
所谓的量词指的是在正则表达式中出现的字符应该重复的次数,一般我们有三个量词符号,分别是
?
、
+
和
*
,它们的表示次数如下图所示:
比如我们有这样一个字符串:
Hello world! hell world!
,现在我察觉到其中一个
hello
少写了一个
o
,但它确实应该是
hello
,因此需要将所有应该是
hello
的字样找到,这时需要用到
?
量词:
$value = 'Hello world! hell world!';while ($value =~ m/\bhello?\b/i){ print "匹配到的字符:$& \n"; $value = $'}
执行结果如下:
再来看一下
+
的用法,现给出一字符串
H1 H12 H123 H566 H H78
,要将该字符串中
H
后面至少带一位数字的单词找出来,用
Perl
代码应该这样写:
$value = 'H1 H12 H123 H566 HR2 H H78';while ($value =~ m/\bH[0-9]+\b/i){ print "匹配到的字符:$& \n"; $value = $'}
从上述输出结果来看,给定字符串中的
HR2
和
H
没有匹配到,这符合我们的要求,如果我们需要将首字母是
H
开头的单词找出来,哪怕只有一个
H
字母也要搜索到,则应该这样写代码:
$value = 'H1 H12 H123 H566 HR2 H H78';while ($value =~ m/\bH[0-9A-Za-z]*\b/i){ print "匹配到的字符:$& \n"; $value = $'}
好了,到目前为止,我们已经学完了第一篇文章中提到的那个正则表达式
m/^hello (.+?)$/i
中所有的信息,现在可以来解释一下它所表达的意思:
m/.../
是
Perl
专用的表示正则表达式的格式,
i
是忽略大小写,
^
是行起首,
$
是行结尾,
hello
是进行原字符串匹配,
(.+?)
是一个字符组,它里面的
.
表示任意字符,紧随其后的
+
是指这个字符至少要匹配一次,而再随其后的
?
则是指前面的那个字符最多可重复
1
次,按这样的解释,我们来对下面一些字符串测试一下匹配效果,以下用
Python
测试:
从上面的测试结果可以看出,
(.+?)
这个正则表达式可以匹配一个空格,也可以匹配任意长度的字串。这样看起来,它与
(.*)
应该是一样的,但却有以下区别:
从上面对比可以看出,
(.+?)
在无法遇到一个字符时,它返回一个空值,但
(.*)
即使无法遇到字符,它也会返回一个空字符串。这一点值得注意。
Perl
语言中有同样的情况,即对于
Hello
而言,
'^hello (.+?)$'
无法匹配到任何字符,匹配失败,但
'^hello (.*)$'
会匹配到一个空字符,匹配成功。
对这个问题的解释是这样的:
.+
本身确保了该位置至少要有一个字符,而其后的
?
只是说明该字符存在0次还是1次,当一个字符都没有时,
.+
就匹配失败了,
?
无法发挥作用;
.*
的意思则是该位置可以有字符也可以没有字符,当没有字符时,显示空字符。
另一点要注意的是,尽管正则表达式只有
(.+?)
,但它是一个字符一个字符连续进行匹配的,因此可以一直匹配到行尾。
小结
本文继续对正则表达式中的元字符进行了分析研究,并对第一篇学习笔记中的正则表达式含义进行了解释。从测试结果来看,同学们应该能感觉到正则表达式功能的强大。