前言
正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言,你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、e-mail地址、TeX命令或任何你想搞定的东西。
在文本中查找模式
import re
patterns = ['this','that']
text = 'Does this text match the pattern?'
for pattern in patterns:
print('Looking for "%s" in ”%s" -> ' % (pattern,text),end="")
if re.search(pattern,text):
print('found a match')
else:
print('no match')
Looking for "this" in ”Does this text match the pattern?" -> found a match
Looking for "that" in ”Does this text match the pattern?" -> no match
search()将pattern放到text 中进行扫描,当找到后返回一个Match对象。如果未找到,返回None
import re
patterns = ['this','that']
text = 'Does this text match the pattern?'
match = re.search(patterns[0],text)
s = match.start()
e = match.end()
print("from %d to %s " % (s,e))
from 5 to 9
这个返回的Match对象包含有关匹配性质的信息,包括原始输入字符串、使用的正则表达式以及出现模式的原始字符串中的位置。
编译表达式
RE包括将正则表达式作为文本字符串使用的模块级函数,但是编译程序经常使用的表达式通常更有效。 函数的作用是:将表达式字符串转换为RegexObject。
import re
regexes = [re.compile(p) for p in ['this','that']]
text = 'Does this text match the pattern?'
for regex in regexes:
print('Looking for "%s" in "%s" -> ' % (regex.pattern,text),end="")
if regex.search(text):
print('found a match')
else:
print('no match')
Looking for "this" in "Does this text match the pattern?" -> found a match
Looking for "that" in "Does this text match the pattern?" -> no match
模块级函数维护编译表达式的缓存,但是缓存的大小是有限的,直接使用编译表达式意味着可以避免缓存查找开销。 通过预编译模块在加载模块时使用的任何表达式,可以将编译工作转移到应用程序启动时间,而不是在程序对用户操作做出响应的时候。
多个匹配项
到目前为止,示例模式都使用search()查找文字字符串的单个实例。 findall()函数返回输入的所有与模式匹配的子字符串,而不重叠。
import re
pattern = 'ab'
text = "abbaaabbbbaaaaa"
for match in re.findall(pattern,text):
print('Found "%s"' % match)#这里返回的match是字符串
Found "ab"
Found "ab"
finditer()返回一个生成Match实例的迭代器,而不是findall()返回的字符串。
import re
pattern = "长江"
text = "长江真长江南好风光"
for match in re.finditer(pattern,text):
s = match.start()
e = match.end()
print('Found "%s" at %d:%d' % (text[s:e],s,e))
Found "长江" at 0:2
Found "长江" at 3:5
模式语法
正则表达式支持比简单文本字符串更强大的模式。模式可以重复,可以锚定到输入中的不同逻辑位置,并且可以紧凑的形式表示,不需要模式中的每个文字字符都存在。所有这些特性都是通过组合文本值和元字符来使用的,这些元字符是re实现的正则表达式模式语法的一部分。下面的例子将使用这个测试程序来探索模式的变化。
import re
def test_patterns(text, patterns=[]):
# 在文本中查找每个模式
for pattern in patterns:
print()
print('Matching "%s"' % pattern)
for match in re.finditer(pattern, text):
s = match.start()
e = match.end()
print(' %2d : %2d = "%s"' % (s, e-1, text[s:e]))
return
if __name__ == '__main__':
test_patterns('abbaaabbbbaaaaa', ['ab','ba'])
Matching "ab"
0 : 1 = "ab"
5 : 6 = "ab"
Matching "ba"
2 : 3 = "ba"
9 : 10 = "ba"
重复
在一个句型中有五种表达重复的方式。
- 后跟元字符*的模式重复零次或多次(允许模式重复零次意味着它根本不需要出现即可匹配)。
- 将*替换为+,该模式必须至少出现一次。
- 使用? 表示图案出现零次或一次。
- 对于特定数量的引用,请在阵列后使用{m},其中m替换为阵列应重复的次数。
- 最后,为了允许可变但有限的重复次数,请使用{m,n},其中m是最小重复次数,n是最大重复次数。 省略n({m,})表示该值至少出现m次,没有最大值。
因此,前三种都可以使用最后一种形式表示,{0,} 等同于 *,{1,} 等同于 +,而{0,1}则与 ? 相同。如果可以的话,最好使用 *,+,或?。很简单因为它们更短也更容易懂。
注意
- 这里的重复是针对后跟元字符的
test_patterns('abbaaabbbbaaaaa', ['ab*',# 没有b或者有多个b
'ab+',# 1个b或者多个b
'ab?',# 没有b或者1个b
'ab{3}',# 必须3个b
'ab{2,3}'# 2个b或者3个b
])
Matching "ab*"
0 : 2 = "abb"
3 : 3 = "a"
4 : 4 = "a"
5 : 9 = "abbbb"
10 : 10 = "a"
11 : 11 = "a"
12 : 12 = "a"
13 : 13 = "a"
14 : 14 = "a"
Matching "ab+"
0 : 2 = "abb"
5 : 9 = "abbbb"
Matching "ab?"
0 : 1 = "ab"
3 : 3 = "a"
4 : 4 = "a"
5 : 6 = "ab"
10 : 10 = "a"
11 : 11 = "a"
12 : 12 = "a"
13 : 13 = "a"
14 : 14 = "a"
Matching "ab{3}"
5 : 8 = "abbb"
Matching "ab{2,3}"
0 : 2 = "abb"
5 : 8 = "abbb"
重复指令的正常处理是在匹配模式时消耗尽可能多的输入。 这种所谓的贪婪行为可能导致更少的单个匹配项,或者匹配项可能包含比预期更多的输入文本。 遵循“?”的重复指令可以关闭贪婪感。
test_patterns('abbaaabbbbaaaaa', ['ab*?',# 没有b或者有多个b
'ab+?',# 1个b或者多个b
'ab??',# 没有b或者1个b
'ab{3}?',# 必须3个b
'ab{2,3}?'# 2个b或者3个b
])
Matching "ab*?"
0 : 0 = "a"
3 : 3 = "a"
4 : 4 = "a"
5 : 5 = "a"
10 : 10 = "a"
11 : 11 = "a"
12 : 12 = "a"
13 : 13 = "a"
14 : 14 = "a"
Matching "ab+?"
0 : 1 = "ab"
5 : 6 = "ab"
Matching "ab??"
0 : 0 = "a"
3 : 3 = "a"
4 : 4 = "a"
5 : 5 = "a"
10 : 10 = "a"
11 : 11 = "a"
12 : 12 = "a"
13 : 13 = "a"
14 : 14 = "a"
Matching "ab{3}?"
5 : 8 = "abbb"
Matching "ab{2,3}?"
0 : 2 = "abb"
5 : 7 = "abb"
从上面可以看出,在允许出现零个b的情况下,使用?禁用任何模式的输入的贪婪消耗,意味着匹配的子字符串不包含任何b个字符。
字符集
字符集是一组字符,任何字符都可以在模式中的该点匹配。 例如,[ab]将匹配a或b。
test_patterns('abbaaabbbbaaaaa',
[ '[ab]', # a或者b
'a[ab]+', # 一个a 后面跟着一个或者多个(a或者b),贪婪模式
'a[ab]+?', # 一个a 后面跟着一个或者多个(a或者b),禁止贪婪模式
])
Matching "[ab]"
0 : 0 = "a"
1 : 1 = "b"
2 : 2 = "b"
3 : 3 = "a"
4 : 4 = "a"
5 : 5 = "a"
6 : 6 = "b"
7 : 7 = "b"
8 : 8 = "b"
9 : 9 = "b"
10 : 10 = "a"
11 : 11 = "a"
12 : 12 = "a"
13 : 13 = "a"
14 : 14 = "a"
Matching "a[ab]+"
0 : 14 = "abbaaabbbbaaaaa"
Matching "a[ab]+?"
0 : 1 = "ab"
3 : 4 = "aa"
5 : 6 = "ab"
10 : 11 = "aa"
12 : 13 = "aa"
表达式的贪婪形式a [ab] +占用了整个字符串,因为第一个字母是a,而每个后续字符都是a或b。
字符集也可用于排除特定字符。 特殊标记^ 表示查找不在后面集合中的字符。
test_patterns('This is some text -- with punctuation.',
[ '[^-. ]+', # 不包含 -, ., 空格这三种的序列
])
Matching "[^-. ]+"
0 : 3 = "This"
5 : 6 = "is"
8 : 11 = "some"
13 : 16 = "text"
21 : 24 = "with"
26 : 36 = "punctuation"
随着字符集变得越来越大,键入每个应该(或不应该)匹配的字符变得单调乏味。 使用字符范围的更紧凑的格式允许您定义字符集,以包括起始点和终止点之间的所有连续字符。
test_patterns('This is some text -- with punctuation.',
[ '[a-z]+', # 小写字母序列
'[A-Z]+', # 大写字母序列
'[a-zA-Z]+', # 小写或者大写的字母序列
'[A-Z][a-z]+', # 一个大写字母后跟一个或多个小写字母序列
])
Matching "[a-z]+"
1 : 3 = "his"
5 : 6 = "is"
8 : 11 = "some"
13 : 16 = "text"
21 : 24 = "with"
26 : 36 = "punctuation"
Matching "[A-Z]+"
0 : 0 = "T"
Matching "[a-zA-Z]+"
0 : 3 = "This"
5 : 6 = "is"
8 : 11 = "some"
13 : 16 = "text"
21 : 24 = "with"
26 : 36 = "punctuation"
Matching "[A-Z][a-z]+"
0 : 3 = "This"
作为字符集的特例,元字符点或句点(.)表示该模式应与该位置的任何单个字符匹配。
test_patterns('abbaaabbbbaaaaa',
[ 'a.', # a后面跟随着任何一个单个字符
'b.', # b后面跟随着任何一个单个字符
'a.*b', # a后面可以跟随任意个字符,最后以b结尾
'a.*?b', # a后面可以跟随任意个字符,最后以b结尾,且是禁止贪婪模式。
])
Matching "a."
0 : 1 = "ab"
3 : 4 = "aa"
5 : 6 = "ab"
10 : 11 = "aa"
12 : 13 = "aa"
Matching "b."
1 : 2 = "bb"
6 : 7 = "bb"
8 : 9 = "bb"
Matching "a.*b"
0 : 9 = "abbaaabbbb"
Matching "a.*?b"
0 : 1 = "ab"
3 : 6 = "aaab"
转义码
一些用“\”开头的特殊字符所表示的预定义字符集通常是很有用的,象数字集,字母集,或其它非空字符集。下列是可用的预设特殊字符:
- \d 匹配任何十进制数;它相当于类 [0-9]
- \D 匹配任何非数字字符;它相当于类 [^0-9]
- \s 匹配任何空白字符((制表符、空格、换行符等等);它相当于类 [ \t\n\r\f\v]
- \S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]
- \w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]
- \W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]
注意 通过在字符前面加上反斜杠(\)来表示转义符。 不幸的是,反斜杠本身必须在普通的Python字符串中转义,这会导致难以阅读的表达式。 使用通过在文字值前面加上r来创建的原始字符串来创建正则表达式可以消除此问题并保持可读性。
test_patterns('This is a prime #1_2 example!',
[ r'\d+', # 数字序列
r'\D+', # 非数字序列
r'\s+', # 空白字符序列
r'\S+', # 非空白字符序列
r'\w+', # 字母数字下划线序列
r'\W+', # 非字母数字下划线序列
])
Matching "\d+"
17 : 17 = "1"
19 : 19 = "2"
Matching "\D+"
0 : 16 = "This is a prime #"
18 : 18 = "_"
20 : 28 = " example!"
Matching "\s+"
4 : 4 = " "
7 : 7 = " "
9 : 9 = " "
15 : 15 = " "
20 : 20 = " "
Matching "\S+"
0 : 3 = "This"
5 : 6 = "is"
8 : 8 = "a"
10 : 14 = "prime"
16 : 19 = "#1_2"
21 : 28 = "example!"
Matching "\w+"
0 : 3 = "This"
5 : 6 = "is"
8 : 8 = "a"
10 : 14 = "prime"
17 : 19 = "1_2"
21 : 27 = "example"
Matching "\W+"
4 : 4 = " "
7 : 7 = " "
9 : 9 = " "
15 : 16 = " #"
20 : 20 = " "
28 : 28 = "!"
要匹配属于正则表达式语法的字符,请对搜索模式中的字符进行转义。
test_patterns(r'\d+ \D+ \s+ \S+ \w+ \W+',
[ r'\\d\+',# 单独“\d”是表示数字序列,“\\d” 表示“\d”;同理“\+” 就表示“+”
r'\\D\+',
r'\\s\+',
r'\\S\+',
r'\\w\+',
r'\\W\+',
])
Matching "\\d\+"
0 : 2 = "\d+"
Matching "\\D\+"
4 : 6 = "\D+"
Matching "\\s\+"
8 : 10 = "\s+"
Matching "\\S\+"
12 : 14 = "\S+"
Matching "\\w\+"
16 : 18 = "\w+"
Matching "\\W\+"
20 : 22 = "\W+"
锚定
除了描述要匹配的模式的内容外,您还可以使用锚定指令指定输入文本中模式应该出现的相对位置。
- ^ 字符串或行的开头
- $ 字符串或行的结尾
- \A 字符串的开头
- \Z 字符串的结尾
- \b 位于单词开头或结尾的空字符串
- \B 不位于单词开头或结尾的空字符串
test_patterns('This is some text -- with punctuation.',
[ r'^\w+', # 位于字符串的开头
r'\A\w+', # 位于字符串的开头
r'\w+\S*$', # 位于字符串的结尾,以字母数字下划线开头,且有零个或多个非空白字符
r'\w+\S*\Z', # 位于字符串的结尾,以字母数字下划线开头,且有零个或多个非空白字符
r'\w*t\w*', # 包含t的单词
r'\bt\w+', # 't'位于单词的开头,且有一个或多个’字母数字下划线‘
r'\w+t\b', # '有一个或多个’字母数字下划线‘,t'位于单词的结尾
r'\Bt\B', # 't'不在开头和结尾
])
Matching "^\w+"
0 : 3 = "This"
Matching "\A\w+"
0 : 3 = "This"
Matching "\w+\S*$"
26 : 37 = "punctuation."
Matching "\w+\S*\Z"
26 : 37 = "punctuation."
Matching "\w*t\w*"
13 : 16 = "text"
21 : 24 = "with"
26 : 36 = "punctuation"
Matching "\bt\w+"
13 : 16 = "text"
Matching "\w+t\b"
13 : 16 = "text"
Matching "\Bt\B"
23 : 23 = "t"
30 : 30 = "t"
33 : 33 = "t"
参考文献
https://pymotw.com/2/re/
https://wiki.ubuntu.com.cn/Python正则表达式指南