相关文章推荐
非常酷的钥匙扣  ·  hive函数 -- split ...·  1 年前    · 
才高八斗的柳树  ·  Convert float into ...·  2 年前    · 
瘦瘦的钢笔  ·  文段左右缩进20磅-掘金·  3 年前    · 
备案 控制台
学习
实践
活动
专区
工具
TVP
写文章
专栏首页 老齐教室 ​Python中的正则表达式(三)
2 0

海报分享

​Python中的正则表达式(三)


《Python正则表达式》这个系列,已经完成了两篇,本文是第三篇,请继续阅读。

量词元字符

量词元字符,在正则表达式中表示匹配若干个字符,可能是0个、1个或者多个。

*

* 在正则表达式中表示匹配 0个或者多个 字符,例如 a* ,就表示匹配0个或者更多个字符 a ,例如可以匹配空字符串、 a aaa 等等。

>>> re.search('foo-*bar', 'foobar')                     # 匹配0个横线
<_sre.SRE_Match object; span=(0, 6), match='foobar'>
>>> re.search('foo-*bar', 'foo-bar')                    # 匹配1个
<_sre.SRE_Match object; span=(0, 7), match='foo-bar'>
>>> re.search('foo-*bar', 'foo--bar')                   # 匹配2个
<_sre.SRE_Match object; span=(0, 8), match='foo--bar'>

上面示例中的正则表达式 foo-*bar ,意思是在 foo 三个字符之后,匹配0个或者更多个 - ,然后是三个字符 bar foobar foo 之后没有 - ,即0个,并且最后三个字符是 bar ,符合正则表达式的规则; foo--bar 则是匹配了两个 - 字符。

前面已经介绍过 . ,表示任何字符(除了换行符),如果和 * 组合,即 .* 表示0个或者任意多个任何字符(除了换行符),换句话说,匹配字符串中任何字符,直到改行结束(遇到换行符)。

>>> re.search('foo.*bar', '# foo $qux@grault % bar #')
<_sre.SRE_Match object; span=(2, 23), match='foo $qux@grault % bar'>

对于字符串 '# foo $qux@grault % bar #' ,按照正则表达式 foo.*bar 进行匹配,从第索引为2的字符开始,符合正则表达式的规则,直到索引为23的字符为止,即匹配了 f (含)和 r (含)之间的所有字符。特别注意观察返回结果中 span match 的值。

+

+ 与上面的 * 类似,但是,它匹配的是 1个或多个 字符,即至少要有一个。

>>> print(re.search('foo-+bar', 'foobar'))              # 0个横线,不匹配
>>> re.search('foo-+bar', 'foo-bar')                    # 1个,匹配
<_sre.SRE_Match object; span=(0, 7), match='foo-bar'>
>>> re.search('foo-+bar', 'foo--bar')                   # 2个,匹配
<_sre.SRE_Match object; span=(0, 8), match='foo--bar'>

通过上面演示的示例,比较 * + 的差异。

?

前面已经有匹配“0个或多个”和“1个或多个”的元字符了,现在应该有匹配 0个或1个 的了,那就是 ?

>>> re.search('foo-?bar', 'foobar')                     # 0个,匹配
<_sre.SRE_Match object; span=(0, 6), match='foobar'>
>>> re.search('foo-?bar', 'foo-bar')                    # 1个,匹配
<_sre.SRE_Match object; span=(0, 7), match='foo-bar'>
>>> print(re.search('foo-?bar', 'foo--bar'))            # 2个,不匹配
None

下面的示例,将前面三个云字符进行比较,进一步理解它们的含义。

>>> re.match('foo[1-9]*bar', 'foobar')
<_sre.SRE_Match object; span=(0, 6), match='foobar'>
>>> re.match('foo[1-9]*bar', 'foo42bar')
<_sre.SRE_Match object; span=(0, 8), match='foo42bar'>
>>> print(re.match('foo[1-9]+bar', 'foobar'))
>>> re.match('foo[1-9]+bar', 'foo42bar')
<_sre.SRE_Match object; span=(0, 8), match='foo42bar'>
>>> re.match('foo[1-9]?bar', 'foobar')
<_sre.SRE_Match object; span=(0, 6), match='foobar'>
>>> print(re.match('foo[1-9]?bar', 'foo42bar'))
None
  • foo[1-9]*bar ,匹配 foo bar 之间有1~9的数字0个或多个。
  • foo[1-9]+bar ,匹配 foo bar 之间有1~9的数字1个或多个。
  • foo[1-9]?bar ,匹配 foo bar 之间有1~9的数字0个或1个。

以上三种量词元字符,还经常组合使用,例如: *? +? ?? 等。

>>> re.search('<.*>', '%<foo> <bar> <baz>%')
<_sre.SRE_Match object; span=(1, 18), match='<foo> <bar> <baz>'>

上面示例中的 <.*> 表示要匹配 < 字符之后的任何字符(除了换行字符),然后是 > 。按照这个规则,匹配了 '%<foo> <bar> <baz>%' 中的 <foo> <bar> <baz> ,但是,如果只想匹配 <foo> 怎么办?

>>> re.search('<.*?>', '%<foo> <bar> <baz>%')
<_sre.SRE_Match object; span=(1, 6), match='<foo>'>

上面使用了 *? ,这种表示可以称为惰性匹配,也将此组合称为惰性量词,对应着,前面提到的 * 等则称为贪婪量词,对应的匹配是贪婪匹配。下面以表格形式列出它们之间的比较(顺便也增加了另外一种“支配量词”,一并列出进行比较)。

贪婪量词

惰性量词

支配量词

描述

*

*?

*+

可以不出现,也可以出现任意次

?

??

?+

可以出现0次或1次

+

+?

++

至少出现1次或以上

{n}

{n}?

{n}+

有且只能出现n次

{n,m}

{n,m}?

{n,m}+

至少出现n次,至多出现m次

{n,}

{n,}?

{n,}+

至少出现n次或以上

  • 贪婪匹配:如果符合要求就一直往后匹配,一直到无法匹配为止。
  • 惰性匹配:一旦匹配到合适的就结束,不在继续匹配下去了。
  • 支配匹配:只尝试匹配整个字符串。如果整个字符串不匹配,则不做进一步。

结合线面示例,理解上述匹配模式。

>>> re.search('<.+>', '%<foo> <bar> <baz>%')
<_sre.SRE_Match object; span=(1, 18), match='<foo> <bar> <baz>'>
>>> re.search('<.+?>', '%<foo> <bar> <baz>%')
<_sre.SRE_Match object; span=(1, 6), match='<foo>'>
>>> re.search('ba?', 'baaaa')
<_sre.SRE_Match object; span=(0, 2), match='ba'>
>>> re.search('ba??', 'baaaa')
<_sre.SRE_Match object; span=(0, 1), match='b'>

{m}

{m} 表示匹配 m 个字符。

>>> print(re.search('x-{3}x', 'x--x'))                # 2个横线,不匹配
>>> re.search('x-{3}x', 'x---x')                      # 3个横线,匹配
<_sre.SRE_Match object; span=(0, 5), match='x---x'>
>>> print(re.search('x-{3}x', 'x----x'))              # 4个横线,不匹配
None

x-{3}x 表示要匹配 x 字符后面有3个 - ,再接 x 的字符串。

{m,n}

{m,n} 表示匹配大于等于m,小于等于n个字符,例如 -{2,4} 可以匹配2个、3个、4个 -

>>> for i in range(1, 6):
...     s = f"x{'-' * i}x"
...     print(f'{i}  {s:10}', re.search('x-{2,4}x', s))
1  x-x        None
2  x--x       <_sre.SRE_Match object; span=(0, 4), match='x--x'>
3  x---x      <_sre.SRE_Match object; span=(0, 5), match='x---x'>
4  x----x     <_sre.SRE_Match object; span=(0, 6), match='x----x'>
5  x-----x    None

有时候会省略m或者n,其含义分别为:

  • {, n} ,等效于 {0, n}
  • {m, } ,匹配大于等于m个字符
  • {, } ,等效 {0, } 或者 *

注意,如果只是 {} ,则表示花括号那个字符了。

>>> re.search('x{}y', 'x{}y')
<_sre.SRE_Match object; span=(0, 4), match='x{}y'>

{m,n}?

如前面表格所示,这是一种惰性量词,它表示匹配m到n个字符,只要匹配到了,就结束。例如:

>>> re.search('a{3,5}', 'aaaaaaaa')
<_sre.SRE_Match object; span=(0, 5), match='aaaaa'>
>>> re.search('a{3,5}?', 'aaaaaaaa')
<_sre.SRE_Match object; span=(0, 3), match='aaa'>

a{3,5} 是贪婪匹配,要匹配到最大长度,即5个字符a;而 a{3,5}? 是惰性匹配,只匹配3个字符a即可。

(未完,待续)

参考资料:https://realpython.com/regex-python/

文章分享自微信公众号:
老齐教室

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!

作者: 老齐
原始发表时间: 2020-05-11
如有侵权,请联系 cloudcommunity@tencent.com 删除。