0. 常用匹配规则

  • ^ 匹配字符串的开头
  • $ 匹配字符串的结尾
  • [...] 匹配一组字符,比如 [abc] 表示匹配小写字母 a 或者 b 或者 c[a-z] 表示匹配所有的小写字母,[0-3] 表示匹配数字 0,1,2,3
  • [^...] 匹配不在 []中的字符
  • . 匹配除了换行符以外的任意字符,当匹配模式指定为 re.S 时则可以匹配任意字符
  • * 匹配零个或者多个表达式
  • + 匹配一个或者多个表达式
  • 匹配零个或者一个前面的表达式,非贪婪方式,也就是尽可能少地匹配
  • {n} 精确匹配 n 个前面的表达式,比如 s{2} 必须匹配字符串中连续的两个 s
  • {n,m} 匹配 n 到 m 个前面的表达式,贪婪方式,也即是尽可能多地匹配。比如 s{1,3} 可以匹配 1 到 3 个 s,但是如果有 3 个就全部匹配
  • {n,} 匹配至少 n 个前面的表达式,比如 s{1,} 等价于 s+s{0,} 等价于 s*
  • a|b 匹配 a 或者 b
  • \d 匹配任意数字,也即 0-9
  • \D 匹配任意非数字
  • \w 匹配字母数字和下划线
  • \W 匹配非字母数字和下划线
  • \s 匹配任意空白字符,也即 Tab \t、换行符 \n、回车符 \r 和 换页符\f
  • \S 匹配任意非空白字符
  • \A 匹配字符串开始
  • \Z 匹配字符串结尾

1. re.match

函数原型:

re.match(pattern, string, flags=0)

re.match 尝试从待匹配字符串的第一个字符开始匹配,如果匹配不成功就返回 None。

  • pattern 匹配的正则表达式
  • string 待匹配的字符串
  • flag=0 指定匹配模式,比如 re.I 使得匹配对大小写不敏感,re.S 使得 . 匹配符可以匹配包含换行符在内的所有字符
content = 'I am seniusen!'
res = re.match('I am.*', content)
print(res.group()) # I am seniusen!
res = re.match('Q am.*', content)
print(res) # None

指定匹配模式

content = 'I AM SENIUSEN!'
res = re.match('i am seniusen', content)
print(res) # None
res = re.match('i am seniusen!', content, re.I)
print(res.group()) # I AM SENIUSEN!

还可以用 | 将两个匹配模式组合起来一起使用

content = '''I AM 
SENIUSEN!'''
res = re.match('i am.*', content, re.I)
print(res.group()) # I AM
res = re.match('i am.*', content, re.I|re.S)
print(res.group())
# I AM 
# SENIUSEN!

用括号可以获取匹配到的内容,res.group(1) 代表第一个括号内匹配的内容,res.group(2) 代表第二个括号内匹配的内容

content = 'My name is seniusen, I was born in 1997, Henan, China.'
res = re.match('My.*?(\d+).*', content)
print(res.group())
# My name is seniusen, I was born in 1997, Henan, China.
print(res.group(1)) # 1997
res = re.match('My.*?(\d{1,3}).*', content)
print(res.group(1)) # 199

上面 .* 可以匹配任意的非换行字符,? 代表非贪婪模式,也就是一旦遇到了数字,就交给括号里面的 \d+ 来匹配,所以得到了 1997

content = 'My name is seniusen, I was born in 1997, Henan, China.'
res = re.match('My.*(\d+).*', content)
print(res.group(1)) # 7
res = re.match('My.*(\d{1,3}).*', content)
print(res.group(1)) # 7

而如果去掉了 ?,则代表是贪婪模式,那么 .* 会一直匹配到 199 才结束,只留给 \d+ 匹配一个数字 7

如果涉及到一些特殊字符,比如 *$,它们在正则表达式匹配中有一些特定含义,那么就不能直接来匹配了,需要使用转义字符 \

content = 'The total cost is $5.00*'
res = re.match('The total cost is $5.00*', content)
print(res) # None,$ 被当作了字符串结尾,无法匹配
res = re.match('The total cost is \$5.00\*', content)
print(res.group()) # The total cost is $5.00*

2. re.search

函数原型:

re.search(pattern, string, flags=0)

其参数和 re.match 一样,但 re.search 尝试搜索整个字符串,然后返回第一个成功的匹配。

content = 'I am seniusen!'
res = re.match('am.*', content)
print(res) # None
res = re.search('am.*', content)
print(res.group(), res.span())
# am seniusen! (2, 14)
# res.span() 返回匹配的位置

3. re.findall

函数原型:

re.findall(pattern, string, flags=0)

re.findall 尝试从待匹配字符串找出所有的匹配项,然后返回一个字符串列表。

content = 'I am seniusen, 3 years ago I was 20 years old!'
res = re.findall('sen', content)
print(res) # ['sen', 'sen']
res = re.findall('\d+', content)
print(res) # ['3', '20']

4. re.sub

函数原型:

re.sub(pattern, repl, string, count=0, flags=0)

re.sub 尝试从待匹配字符串中找出所有的匹配并进行替换。

  • repl 替换的字符串,也可以是一个函数
  • count=0 替换的最大次数,默认为零表示替换所有的匹配
content = 'I am seniusen, 3 years ago I was 20 years old!'
res = re.sub('\d+', 'xxx', content)
print(res)
# I am seniusen, xxx years ago I was xxx years old!
res = re.sub('(\d+)\s(.*?)s', r'\1x \2s', content)
print(res)
# I am seniusen, 3x years ago I was 20x years old!
res = re.sub('(\d+)\s(.*?)s', r'\1x \2s', content, 1)
print(res)
# I am seniusen, 3x years ago I was 20 years old!

上面第一次是将字符串中所有的数字替换为 xxx,第二次是在数字后添加一个 x。其中 \1 代表前面第一个括号中匹配的内容,而 \2 代表前面第二个括号中匹配的内容,注意这里避免转义要添加上 r。第三次我们则设置只能替换一次。

def num2word(matched):
    word = {'3': 'three', '20': 'twenty'}
    return word[matched.group('num')]

content = 'I am seniusen, 3 years ago I was 20 years old!'
res = re.sub('(?P<num>\d+)', num2word, content)
print(res)
# I am seniusen, three years ago I was twenty years old!

上面的 repl 参数是一个函数,表示将匹配到的数字转化为其对应的英文单词。其中 ?P<num> 表示给匹配到的内容进行一个分组,然后通过 matched.group('num') 就可以很方便地取出这个匹配结果。上面的代码和下面这段等价。

def num2word(matched):
    word = {'3': 'three', '20': 'twenty'}
    return word[matched.group()]

content = 'I am seniusen, 3 years ago I was 20 years old!'
res = re.sub('\d+', num2word, content)
print(res)

5. re.compile

函数原型:

re.compile(pattern[, flags])

re.compile 用于构建一个正则表达式对象,便于重复使用。

content = 'I am seniusen, 3 years ago I was 20 years old!'
pattern = re.compile('\d+')  # 匹配至少一个数字
res = pattern.match(content)
print(res)  # None 从头开始,无法匹配数字
res = pattern.search(content)
print(res.group())  # 3 匹配第一个数字
res = pattern.findall(content)
print(res)  # ['3', '20'] 匹配到所有的数字
res = pattern.findall(content, 0, 16)
print(res)  # ['3'] 限制在第 1 到第 16 个字符之间查找