Python和正则匹配
正则匹配
预定义字符集
利用\转移字符加上字母可以表示通常使用的预定义字符集,下面是常用的预定义字符集:
\w(word):匹配字母、数字、下划线。等价于[A-Za-z0-9_]\W:匹配非字母、非数字、非下划线。等价于[^A-Za-z0-9_]\s(space): 匹配所有空白符。等价于[ \t\n\r\f\v](注意这里可以匹配空格)\S: 匹配所有非空白符。利用[\s\S]可以匹配任一字符\d(digital): 匹配所有数字。等价于[0-9]\D: 匹配所有非数字。等价于[^0-9]
特殊字符
部分字符具有特殊的含义。
-
\: 转义字符,要单独表示匹配特殊字符时,在前面加上转移字符 -
.: 匹配任意单个字符,除了换行符\n -
[: 标记一个中括号表达式的开始,一对中括号[]代表一个集合,每次匹配会选择一个字符匹配[]中的内容。可以利用-表示区间,在中括号之中特殊字符大部分都会失去特殊含义(除了转移字符\、放在中括号第一位的^)[abcd] 匹配 a 或 b 或 c 或 d [.*\] 匹配 . 或 * 或 \ [a-z] 匹配 a-z之间任意一个字符 [a-zA-Z0-9] 匹配 任意一个英文字符或数字 [][] 匹配 ] 或 [ -
(:标记一个子表达式的开始(也就是对于python中的分组group),利用()可以捕获匹配到的正则表达式中的内容,子表达式可以获取以后使用。分组group相关的具体内容会在下面细说。 -
{:标记限界符表达式的开始,利用{n,m}的形式可以指定重复形式,具体见下文 -
^: 在中括号表达式中,放在第一个表示该中括号表达式的补集,否则放在表达式的开头时,代表要匹配输入字符串的首部[^][] 匹配 除了 [ 和 ] 之外的任一字符 [^0-9] 匹配任一非数字字符 [a-z^] 匹配 任一小写英文字符 or ^ ^abc 匹配 abcd 而不匹配 dabc -
$: 放在表达式的尾部时,表示匹配输入字符串的尾部^ab*c$ 匹配以a开头、中间有0或多个b、以c结尾的字符串 [abc]$ 匹配在尾部的a或b或c的单个字符 -
|:或运算符,表示需要匹配的元素要么在|的左侧,要么在|的右侧,使用|经常会与()结合,例如(aa|bb)会匹配aa或bb,此时由于子表达式的副作用会捕获匹配的aa或bb,通过使用(?:xx|yy)的方式可以取消捕获。
限定符(控制匹配重复)
利用限定符指示一个正则表达式中某一子表达式要出现几次。
-
*: 匹配前面的子表达式零次或多次(aca)* 匹配 aca or acaaca or acaacaaca or 空字符串 a*cd 匹配 aacd or cd or aaaaacd -
+: 匹配前面的子表达式一次或多次(abc)+ 匹配 abc or abcabc -
?: 匹配前面的子表达式零次或一此(abc)? 匹配 abc 或 空字符常 -
{n}: 匹配前面的子字符串n次a{5} 匹配 aaaaa (pix){3} 匹配 pixpixpix -
{n,}: 匹配前面的子字符串n次或更多次(pix){2,} 匹配 pixpix or pixpixpix or ... -
{n,m}: 匹配前面的子字符串至少n次,至多m次a{2,4} 匹配 aa or aaa or aaaa a+ 等价于 a{1,} a* 等价于 a{0,} a? 等价于 a{0,1}
其中 + 、*、? 都是贪婪的,它们会尽可能匹配多的内容,在其后面加上?可以实现非贪婪或最小匹配。例如a+?、\w*?、x?? 。
实用正则匹配
- 邮箱匹配:包括大小写字母、下划线、数字、点号、中划线
[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9_+]+
- 匹配身份证号码:xxxxxxyyyyMMddxyzh
xxxxxx:六位地区 [1-9]\d{5}
yyyy:出生年份18xx-21xx年 (18|19|20|21)\d{2}
MM:出生月份 ((0[1-9])|10|11|12)
dd:初始日期 (([0-2][1-9])|10|20|30|31)
xyz:三位顺序码 \d{3}
h:校验码 [0-9Xx]
[1-9]\d{5}(18|19|20|21)\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]
Python中使用正则匹配
为了防止\在python的字符串中的转义语义影响到正则表达式的判断,对字符串使用r前缀(raw)保证传入个compile()的字符串是原始语义
常见使用方式如下
import re
regular = r'[a-z]+' # 正则表达式
p = re.compile(regular) # 构造出的正则表达式对象
构造出正则表达式对象后,该对象有以下方法,其中pos和endpos用于限定匹配位置(这些方法在re中也有直接的实现,差别就是入参多填写一个要匹配的正则表达式,例如re.findall(pattern, string, flags=0):
| 方法 | 作用 |
|---|---|
| match(string[, pos[, endpos]]) | 从字符串的开头开始匹配 |
| search(string[, pos[, endpos]]) | 匹配字符串,返回第一个匹配结果 |
| findall(string[, pos[, endpos]]) | 匹配字符串,返回所有的匹配结果 |
| finditer(string[, pos[, endpos]]) | 匹配字符串,以iterator的方式返回匹配结果 |
match() & search()
match() 和 search() 如果没有匹配成功返回 None ,如果匹配成功返回一个匹配对象Match Object。Match Object具有以下方法:
| 方法 | 作用 |
|---|---|
| group([group1, ...]) | 返回正则匹配的字符串。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回) |
| start() | 返回匹配的开始位置 |
| end() | 返回匹配的结束位置 |
| span() | 返回包含匹配 (start, end) 位置的tuple |
| groups() | 返回捕获的分组构成的tuple |
| 例如: |
import re
regular = r'[a-z]+' # 正则表达式
p = re.compile(regular) # 构造出的正则表达式对象
m = p.match('abcdABCD')
print(m.group()) # abcd
print(m.start()) # 0
print(m.end()) # 4
print(m.span()) # (0,4)
其中对于group()会返回对应的捕捉分组,group()和group(0)默认返回匹配到的正则表达式,而group(i)会返回第i个捕获分组,groups()会返回所有分组构造的tuple,如下所示:
p = re.compile(r'(a)(b)(c)d(e)')
m = p.match('abcde')
print(m.group()) # abcde
print(m.group(0)) # abcde
print(m.group(1)) # a 返回第1个分组a
print(m.group(4)) # e 返回第4个分组e
for g in m.groups(): # 返回catch到的分组的tuple,若没有设置分组捕获则为空
print(g) # a b c e
p = re.compile(r'ack')
m1 = p.match('no ack no ack')
m2 = p.search('no ack no ack')
print(m1) # None match只会从字符串的开头开始尝试匹配
print(m2) # <re.Match object; span=(3, 6), match='ack'> search则会扫描整个字符串,尝试匹配成功1次后返回对应的Match对象,可以看到第二个ack没有匹配到
常用的使用方法如下:
p = re.compile( 'regular string' )
m = p.search( 'string goes here' )
if m: # 匹配成功
print('Search found: ', m.group())
for g in m.groups(): # 查看捕获的group
print('catch group ',g)
else: # 没有匹配成功
print('No match')
findall() & finditer()
findall()会匹配字符串并返回所有的匹配结果,若未设置group捕获则会返回匹配成功的字符串构成的list,若设置了group捕获则会返回group构成的tuple对应的list
re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
# ['foot', 'fell', 'fastest'] 没有设置group捕获,返回匹配成功的字符串构成的list
re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
# [('width', '20'), ('height', '10')] 设置了group捕获,返回group构成的tuple对应的list
而finditer()的功能和findall()是一样的,只是返回的结果是一个callable_iterator,迭代器中的内容为Match Object,使用finditer()可以使用到Match Object的函数(例如span()):
iter = re.finditer(r'(\w+)=(\d+)', 'set width=20 and height=10')
print(type(iter)) # <class 'callable_iterator'>
for m in iter:
print(type(m)) # <class 're.Match'>
print(m.groups()) # ('width', '20') ('height', '10')
print(m.group()) # 'width=20' 'height=10'
print(m.span()) # (4, 12) (17, 26)
常见的使用方法如下:
iter = re.finditer('regul str','matched str')
if iter: # 匹配成功
for m in iter: # 遍历callable_iterator
print('match str is ',m.group())
for g in m.groups(): # 遍历groups()
print('catch group ',g)
else: #匹配失败
print('no match')
分组
在复杂的正则中,很难跟踪组号。利用命名分组可以通过名称的方式引用groups。使用(?P<name>...)的方式可以获取到name对应的group,同时可以让Match Object使用 groupdict()方法获取一个对应的map
m = re.search(r'(?P<first>\w+) (?P<last>\w+)', 'Jane Doe') # 分组命名 first 和 last
print(m.groups()) # ('Jane', 'Doe')he
print(m.groupdict()) # {'first': 'Jane', 'last': 'Doe'}
print(m.group('last')) # 'Doe'
通过合理的使用分组命名可以快速提取需要的信息。
使用regex101可以在线检验自己的正则表达式。

浙公网安备 33010602011771号