正则表达式(Regular Expression,常简写为regex、regexp或re),是一种简练地描绘一组字符的方式,可用于高效、精确地执行字符串检索、替换等任务。
元字符
语法
描述
语法
描述
语法
描述
语法
描述
[abc]
单个字符:a或b或c
[^abc]
a/b/c以外的单个字符
[a-zA-Z0-9]
范围内的字符
.
任意字符(除\n)
\s
空白符(等价于[ \f\n\r\t\v])
\S
非空白符
\d
数字字符(十进制)
\D
非数字字符
\w
单词(字母,数字,下划线,中文)
\W
非单词
\b
单词边界
\B
非单词边界
^ \A
开头
$ \Z
结尾
(…)
分组
(a|b)
择一匹配:a或b
a*
重复0次或多次
a+
重复1次或多次
a?
重复0次或1次
a{n}
重复n次
a{n,}
重复n次或多次
a{n,m}
重复n到m次
?
非贪婪匹配
(?:abc)
非捕获分组
(?=abc)
正向匹配abc
(?!abc)
正向不匹配abc
\xhh
十六进制hh字符
\uhhhh
十六进制hhhh字符
\u{hhhh}
十六进制hhhh字符(设置u标志)
(?#comment)
注释
修饰符
i - IgnoreCase:不区分大小写
m - MultiLine:多行匹配
s - DotAll:.
匹配所有字符(包括\n
)
print (re.match(r'a' , 'A' )) print (re.match(r'a' , 'A' , re.I)) print (re.findall(r'^test-\w*' , 'test-google\ntest-baidu\ntest-weibo' )) print (re.findall(r'^test-\w*' , 'test-google\ntest-baidu\ntest-weibo' , re.M)) print (re.match(r'.*' , 'a\nb\nc' )) print (re.match(r'.*' , 'a\nb\nc' , re.S))
贪婪与懒惰 当正则表达式中包含限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符(主动回溯),这被称为贪婪匹配 ;相反的是,在表达式能得到匹配的前提下匹配尽可能少的字符,这就是懒惰匹配 。在使用上,只需在限定符后添加?
,就可以将贪婪限定符转换为惰性限定符。
限定符:限定前面的表达式出现的次数。
1 2 3 4 5 6 print (re.match(r'^a{2,5}$' , 'aaaaa' )) print (re.match(r'^a{2,5}' , 'aaaaa' )) print (re.match(r'^a{2,5}?$' , 'aaaaa' )) print (re.match(r'^a{2,5}?' , 'aaaaa' ))
分组和后向引用 捕获分组 (exp) :匹配表达式exp,并捕获文本到自动命名的组里。
(?Pexp) :匹配表达式exp,并捕获文本到名称为name的组里。
在使用小括号进行分组后,每个分组会自动拥有一个组号。组号分配会从左向右扫描两遍:先给未命名组分配,第一个出现的分组组号默认为1,第二个为2,以此类推,之后给命名组分配分组名称。分组0代表全局分组,对应着整个正则表达式匹配的结果。
1 2 3 4 5 6 7 8 it = re.finditer(r'age:(?P<agegroup>\d+),name:(\w+)' , 'age:13,name:Tom;age:18,name:John' )for m in it: print ('------' ) print (m.group()) print (m.group(0 )) print (m.group(1 )) print (m.group('agegroup' )) print (m.group(2 ))
非捕获分组 (?:exp):匹配表达式exp,不捕获匹配的文本,也不给此分组分配组号。
1 2 3 4 print (re.findall(r'age:(\d+),name:(\w+)' , 'age:13,name:Tom;age:18,name:John' )) print (re.findall(r'age:(?:\d+),name:(\w+)' , 'age:13,name:Tom;age:18,name:John' ))
后向引用 所谓后向引用,就是在后式中对前面出现过的分组再一次引用,可用于重复搜索前面某个分组匹配的文本。
通过索引引用: \1
表示引用第一个分组,\2
表示引用第二个分组,以此类推,\n
表示引用第n个分组。
通过命名分组名引用:命名 (?Pexp),引用 (?P=name) 。
1 2 3 4 5 6 7 8 9 print (re.findall(r'\b(\w+)\b\s+\1\b' , 'this is a test to match: Go Go home home' )) print (re.findall(r'\b(?P<testgroup>\w+)\b\s+(?P=testgroup)\b' , 'this is a test to match: Go Go home home' )) s = '12345678900' s = s[::-1 ] s = re.sub(r'(...)' , r'\1,' , s)print (s[::-1 ])
零宽断言 断言用来声明一个应该为真的事实,正则表达式中只有当断言为真时才会继续进行匹配。零宽断言是一种零宽度的匹配,它匹配到的内容不会保存到匹配结果中,最终匹配结果只是一个位置 。
零宽断言用于查找在某些内容(但并不包括这些内容)之前或者之后的东西,这个位置应该满足一定的条件(即断言)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 print (re.findall(r'\b\w+(?=ing\b)' , 'reading and watching' )) print (re.findall(r'\b\w+(?=ing\b)' , 'reading and watch' )) print (re.findall(r'\b\w+(?=ing\b)' , 'reading and watching1' )) print (re.findall(r'\b.+(?=ing\b)' , 'reading and watching' )) print (re.findall(r'(?<=\bread)\w+\b' , 'reading and watching' )) print (re.findall(r'(?<=\bread)\w+\b' , 'reading1 and watching' )) print (re.findall(r'(?<=\bread)\w+\b' , 'reading1 and reading2' )) print (re.findall(r'(?<=\bread).+\b' , 'reading and watching' )) print (re.findall(r'\b\w+d(?!ing\b)\w*' , 'reading and watching' )) print (re.findall(r'\b\w+d(?!ing\b)\w*' , 'read and watching' )) print (re.findall(r'\b\w+d(?!ing\b)\w*' , 'reading1 and watching' )) print (re.findall(r'\b\w+d(?!ing)\w*' , 'reading1 and watching' )) print (re.findall(r'\b\w+(?!ing\b)\w*' , 'reading and watching' )) print (re.findall(r'\w+(?<!\brea)d\w*\b' , 'reading and watching' )) print (re.findall(r'\w+(?<!\brea)d\w*\b' , 'eading and watching' )) print (re.findall(r'\w+(?<!\brea)d\w*\b' , '1reading and watching' )) print (re.findall(r'\w+(?<!rea)d\w*\b' , '1reading and watching' )) print (re.findall(r'\w+(?<!\brea)\w*\b' , 'reading and watching' ))