4 道练习题·预计 35 分钟·做对一题解锁下一段
正则表达式(Regular Expression,简称 regex) 是一种用「特殊字符序列」描述字符串模式的语言。
它能回答这一类问题:
Python 自 1.5 版本起就内置了 re 模块,提供 Perl 风格的正则功能。
先看一个反面例子。要判断字符串里是否包含某段固定文字,根本不需要正则:
a = '两点水|twowater|liangdianshui|草根程序员'
print('两点水' in a) # True
print(a.index('两点水') > -1) # True这种纯「子串查找」用 in 就够了。
正则真正发力的地方是——你不知道具体的字符是什么,但知道它们的形状。比如:
「找出字符串里所有的连续小写字母」
import re
a = '两点水|twowater|liangdianshui|草根程序员'
print(re.findall('[a-z]+', a))
# ['twowater', 'liangdianshui'][a-z]+ 就是一条「规则」:
[a-z] 代表「任何一个小写字母」+ 代表「连续出现一次以上」合起来就是「一段小写字母」。re.findall 把字符串里所有匹配的片段都列出来。
re.match / re.search / re.findall:三个最常用的函数(...):从匹配里提取片段re.sub:基于规则做替换学完你就有能力写出「找手机号」「抓邮箱」「清洗多余空格」这类常用工具了。
正则之所以「叫正则」,是因为它有一套自己的小语法。这里只学最常用的两类:字符集 / 元字符 和 量词。
[...]:「或」的关系方括号 [...] 里写一组字符,表示匹配其中任意一个。
import re
a = 'uav,ubv,ucv,uwv,uzv,uov'
# 取 u 和 v 中间是 a / b / c 的
print(re.findall('u[abc]v', a))
# ['uav', 'ubv', 'ucv']
# 连续字母可以用 - 缩写
print(re.findall('u[a-c]v', a))
# ['uav', 'ubv', 'ucv']
# 取反:u 和 v 中间不是 a / b / c 的
print(re.findall('u[^abc]v', a))
# ['uwv', 'uzv', 'uov']要点:
[abc] 是 a 或 b 或 c[a-z] 是连续范围:所有小写字母[^abc] 在开头加 ^ 表示取反下面这几个是最常用的预定义字符集,建议记下来:
| 写法 | 等价 | 含义 |
|---|---|---|
\d | [0-9] | 任何数字 |
\D | [^0-9] | 非数字 |
\w | [A-Za-z0-9_] | 单词字符(字母 / 数字 / 下划线) |
\W | [^A-Za-z0-9_] | 非单词字符 |
\s | [ \t\n\r\f\v] | 空白字符 |
\S | 非空白字符 | |
. | 任意一个字符(除换行外) |
import re
a = 'hi 123-abc'
print(re.findall(r'\d', a)) # ['1', '2', '3']
print(re.findall(r'\w', a)) # ['h', 'i', '1', '2', '3', 'a', 'b', 'c']注意:正则字符串里反斜杠
\容易和 Python 自身的转义冲突——养成习惯,正则全部用r'...'原始字符串。
光匹配「一个」字符不够用。我们要表达重复几次,就要用量词:
| 写法 | 含义 |
|---|---|
? | 0 次或 1 次 |
+ | 1 次或多次 |
* | 0 次或多次 |
{n} | 恰好 n 次 |
{n,m} | n 到 m 次 |
{n,} | 至少 n 次 |
例子——找出 4 到 7 个字母组成的英文单词:
import re
a = 'java*&39android##@@python'
print(re.findall('[a-z]{4,7}', a))
# ['java', 'android', 'python']默认量词是贪婪的——能多吃就多吃:
print(re.findall('[a-z]{4,7}', 'android'))
# ['android'] ← 一次吃满 7 个在量词后面加 ? 变成非贪婪——能少吃就少吃:
print(re.findall('[a-z]{4,7}?', 'android'))
# ['andr'] ← 只吃 4 个就回头| 贪婪 | 非贪婪 | 描述 |
|---|---|---|
? | ?? | 0 次或 1 次 |
+ | +? | 1 次或多次 |
* | *? | 0 次或多次 |
{n,m} | {n,m}? | n 到 m 次 |
经验:写正则时默认是贪婪——这往往不是你想要的。要小心,必要时加
?。
字符串 'abc123def456ghi789' 里夹杂着一些连续的数字段。
请用 re.findall 把所有连续数字段抓出来,赋给变量 nums,然后 print(nums)。
输出应该是:
['123', '456', '789']
提示:连续数字可以用 \d+。