正则表达式的简单使用

入门

元字符及其反义

元字符是正则表达式的基本元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 匹配除换行符以外的任意字符
.
# 匹配字母或数字或下划线或汉字【character】
\w
# 匹配任意不是(字母或数字或下划线或汉字)的字符
\W
# 匹配任意的空白符【space】
\s
# 匹配任意是不是空白符的字符
\S
# 匹配数字【digit】
\d
# 匹配任意非数字的字符
\D
# 匹配单词的开始或结束【boundaries】
\b
# 匹配不是单词开始或结束的位置
\B
# 匹配字符串的开始
^
# 匹配字符串的结束
$
# 匹配除了x以外的任意字符
[^x]
# 匹配除了aeiou这几个字符以外的任意字符
[^aeiou]

# example

# 匹配有 abc 开头的字符串
\babc
^abc

# 匹配 8 位数字的 QQ 号码
^\d\d\d\d\d\d\d\d$

# 匹配 1 开头 11 位数字的手机号码
^1\d\d\d\d\d\d\d\d\d\d\d\d$

重复限定符

用来处理重复的元素,简化正则写法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 重复零次或更多次
*
# 重复一次或更多次
+
# 重复零次或一次
?
# 重复 n 次
{n}
# 重复 n 次或更多次
{n,}
# 重复 n 次到 m 次
{n,m}

# example

# 匹配 8 位数字的 QQ 号码
^\d{8}$

# 匹配 1 开头 11 位数字的手机号码
^1\d{10}$

# 匹配银行卡号是 14~18 位的数字
^\d{14, 18}$

# 匹配以 a 开头的,0 个或多个 b 结尾的字符串
^ab*$

分组

用于同时限定多个元素。

1
2
3
4
5
6
7
# 使用小括号 () 进行分组
()

# example

# 匹配字符串中包含 0 到多个 ab 开头
^(ab)*

转义

用于表示正则里占用的特殊字符。

1
2
3
4
5
6
7
# 转义的字符前加斜杠 \ 进行转义标识
\

# example

# 匹配字符串中包含 0 到多个 (ab) 开头
^(\(ab\))*

条件或

1
2
3
4
5
6
7
# 使用符号竖杠 | 标识正则中的条件或
|

# example

# 联通有 130/131/132/155/156/185/186/145/176 等号段,匹配一个联通的号码
^(130|131|132|155|156|185|186|145|176)\d{8}$

区间

元素逐个枚举会比较复杂,所以用区间简化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用符号中括号 [] 标识区间
[]

# example

# 限定 0 到 9
[0-9]

# 限定 A 到 Z
[A-Z]

# 限定某些字符和数字
[165af]

# 联通有 130/131/132/155/156/185/186/145/176 等号段,匹配一个联通的号码
^((13[0-2])|(15[5-6])|(18[5-6])|145|176)$

进阶

零宽断言

零宽:不占位,断言:用于定位。

  • 正向先行断言(正前瞻)
    • 语法:(?=pattern)
    • 作用:匹配 pattern 表达式的前面内容,不返回本身。
  • 正向后行断言(正后顾):
    • 语法:(?<=pattern)
    • 作用:匹配 pattern 表达式的后面的内容,不返回本身。
  • 负向先行断言(负前瞻)
    • 语法:(?!pattern)
    • 作用:匹配非 pattern 表达式的前面内容,不返回本身。
  • 负向后行断言(负后顾)
    • 语法:(?<!pattern)
    • 作用:匹配非 pattern 表达式的后面内容,不返回本身。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> import re

>>> line = "<span class=\"read-count\">阅读数:641</span>"

# 正前瞻
>>> value = re.search(r".+(?=</span>)", line)
>>> print(value.group())
<span class="read-count">阅读数:641

# 正前瞻
>>> value = re.search(r"\d+(?=</span>)", line)
>>> print(value.group())
641

# 正后顾
>>> value = re.search(r"(?<=<span class=\"read-count\">阅读数:)\d+", line)
>>> print(value.group())
641


>>> line = "我爱祖国,我是祖国的花朵"

# 负前瞻
>>> value = re.search(r"祖国(?!的花朵)", line)
>>> print(value.group())
祖国

# 负后顾
>>> value = re.search(r"(?<!我爱)祖国", line)
>>> print(value.group())
祖国

捕获与非捕获

用于分组匹配。

  • 数字编号捕获组
    • 语法:(exp)
    • 解释:每个括号表示一个分组,第0个分组表示整个表达式。
  • 命名编号捕获组
  • 非捕获组
    • 语法:(?:exp)
    • 解释:用于标记这个分组不需要捕获。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> import re

>>> line = "020-85653333"

# 数字编号捕获组
>>> value = re.search(r"(0\d{2})-(\d{8})", line)
>>> print(value.group(0))
020-85653333
>>> print(value.group(1))
020
>>> print(value.group(2))
85653333

# 命名编号捕获组
>>> value = re.search(r"(?P<区号>0\d{2})-(?P<号码>\d{8})", line)
>>> value
<re.Match object; span=(0, 12), match='020-85653333'>
>>> print(value.group('区号'))
020
>>> print(value.group('号码'))
85653333

# 非捕获组
>>> value = re.search(r"(?:0\d{2})-(\d{8})", line)
>>> value.group(0)
'020-85653333'
>>> value.group(1)
'85653333'

反向引用

「捕获与非捕获」会返回一个捕获组,这个分组既可以在正则外部进行引用,也可以在正则表达式内部进行引用,实现一些较为复杂的匹配。在正则表达式内部引用捕获组,称之为反向引用。

  • 数字编号反向引用
    • \number
  • 命名编号反向引用
    • \'name'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> import re

>>> line = "aabbbbgbddesddfiid"

# TASK: 捕获成对的字母,如 aa、bb
# 思路: 匹配字母用\w,重复用 \1,即(\w)\1。

value = re.search(r"(\w)\1", line)
>>> value.group(0)
'aa'

# 迭代输出所有
>>> pattern = re.compile(r"(\w)\1")
>>> for value in re.finditer(pattern, line):
... print(value.group(0))
...
aa
bb
bb
dd
dd
ii

贪婪和非贪婪

  • 贪婪模式:当正则表达式中包含能接受重复的限定符时,匹配尽可能多的字符。即能多不会少。
  • 非贪婪模式:当正则表示中包含能接受重复的限定符时,匹配尽可能少的字符。即能少不会多。
    • 懒惰量词:在贪婪量词后面加 ?
  • 多个贪婪量词在一起时,会按深度优先原则,也就是从左到右,优先满足左边的贪婪量词,剩余再分配下一个量词匹配。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 重复零次或更多次
*
# 重复一次或更多次
+
# 重复零次或一次
?
# 重复 n 次
{n}
# 重复 n 次或更多次
{n,}
# 重复 n 次到 m 次
{n,m}

# 重复零次或更多次,尽可能少重复
*?
# 重复一次或更多次,尽可能少重复
+?
# 重复零次或一次,尽可能少重复
??
# 重复 n 次,尽可能少重复
{n}?
# 重复 n 次或更多次,尽可能少重复
{n,}?
# 重复 n 次到 m 次,尽可能少重复
{n,m}?

参考资料