正则表达式

什么时候会遇到正则表达式?

正则表达式是用来匹配字符串的一种工具。举例:

  • 如果我打开了一个文档,想要找到里面所有的电话号码(格式为xxx-xxxxxxx),如何实现?
  • 我建立了一个网站,用户注册的时候需要输入邮箱,如何检查用户输入的是否为正确的邮箱格式?

  • 这些都是经常遇到的问题,这个时候就可以用正则表达式了。

什么是正则表达式?

当我们在win10里查找某个目录下所有的txt文件时,大部分人都会直接搜索*.txt,这个*.txt就是正则表达式,表示后缀为*.txt的所有文件名。这是简单的一个例子,实际使用时,一个正则表达式可能是很复杂的,但都是由下面这些元素组成的:

  • 单个英文字母或数字,表示精确匹配,例如上例中的txt
  • \d表示一个数字,\w表示一个数字或字母,\s表示至少一个空格
  • .匹配任意一个字符
  • *表示任意个字符(包括0个)
  • +表示至少一个字符
  • ?表示0个或1个字符
  • {n}表示n个字符
  • {n,m}表示n-m个字符
  • []表示范围,例如[0-9]表示任意一个数字

  • 完整的正则表达式规则很长,上面的只是部分常用规则,完整规则可以参考re—正则表达式操作.

一些例子

我们可以尝试一些简单的例子:

  • \d{3}\s+\d{3,8}

我们从左往右读:\d{3}表示三个数字,\s+表示至少一个空格,\d{3,8}表示3到8个数字。

  • [0-9a-zA-Z\_]

匹配由一个数字、字母或者下划线组成的字符串,比如’a’,’_’,’3’等等

在Python中使用正则表达式

Python中我们一般用re模块来使用正则表达式。

匹配

1
2
3
4
5
6
import re
test = '用户输入的字符串'
if re.match(r'正则表达式', test):
print('ok')
else:
print('failed')

这里的re.match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None
由于Python本身也使用\来表示转义,所以为了避免考虑这个问题,我们使用r前缀来禁用转义符的功能:

1
test = r'用户输入的字符串'

进一步地,re模块还提供了其他的功能。

分割

1
2
>>> 'a b c'.split(' ')
['a', 'b', '', '', 'c']

Python内置的split函数会将每一个空格分割开来,如果我们想将连续的空格分割为一个,则可以使用re模块的split()函数:

1
2
>>> re.split(r'\s+', 'a b c')
['a', 'b', 'c']

分组

()来表示一个组。

1
2
3
4
5
6
7
8
9
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'

贪婪匹配和非贪婪匹配

默认情况下是贪婪匹配,也就是尽可能地匹配更多的字符。

1
2
3
4
>>> s='hello 1234567 world'
>>> res = re.match('he.*(\d+).*rld$',s)
>>> print(res.group(1))
'7'

这里是因为.*会匹配多个任意字符,在默认的贪婪模式下,它会一直匹配到满足后面格式的最低要求,后面是\d+,所以它只给后面留了一个数字7。开启非贪婪模式,只需要在后面加一个?

1
2
3
4
>>> s='hello 1234567 world'
>>> res = re.match('he.*?(\d+).*rld$',s)
>>> print(res.group(1))
'1234567'

编译

当我们在Python中使用正则表达式时,re模块内部会干两件事情:1)编译正则表达式,如果正则表达式的字符串本身不合法,会报错;2)用编译后的正则表达式去匹配字符串。如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配。

1
2
3
4
5
6
7
8
>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')