1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com
  1. Python 系列(一) 从零开始认识 Python,入门其实没那么难
  2. Python 系列(二) 学 Python 的第一步,把开发环境搭建好
  3. Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
  4. Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
  5. Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
  6. Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
  7. Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
  8. Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
  9. Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
  10. Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
  11. Python 系列(十一) list、tuple、set、dict,到底该怎么选?
  12. Python 系列(十二) List 列表实战,搞定数据增删改查与批量处理
  13. Python 系列(十三) Tuple 元组实战,搞懂不可变特性与 enumerate() 遍历
  14. Python 系列(十四) Set 集合实战,搞定数据去重与集合运算
  15. Python 系列(十五) Dict 字典详解,掌握快速查找与键值操作
  16. Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
  17. Python 系列(十七) 自定义函数入门,让重复代码一次编写多次使用
  18. Python 系列(十八) 函数参数详解,搞懂位置参数、关键字参数和默认参数
  19. Python 系列(十九) 从 *args 到 **kwargs,让函数调用更灵活
  20. Python 系列(二十) 变量作用域是什么?一文搞懂 global 和 nonlocal
  21. Python 系列(二十一) 搞懂变量、对象和引用,理解函数参数传递机制
  22. Python 系列(二十二) map() 和 filter() 详解,轻松实现批量数据处理与筛选
  23. Python 系列(二十三) 嵌套函数与 lambda 表达式详解,搞懂作用域与闭包
  24. Python 系列(二十四) 搞懂递归函数,从基线条件到实际应用
  25. Python 系列(二十五) 聊聊模块与 import,让代码告别重复编写
  26. Python 系列(二十六) Python 模块入门,从 import 到模块复用
  27. Python 系列(二十七) Python dir() 函数详解,快速查看模块中的函数与属性
  28. Python 系列(二十八) 不会用 Python 标准库?教你快速查找模块和函数
  29. Python 系列(二十九) 搞懂 Python sys 模块,让脚本更适合自动化运维
  30. Python 系列(三十) 为什么 0.1 + 0.2 不等于 0.3?彻底搞懂 Decimal
  31. Python 系列(三十一) 搞懂 datetime 日期时间,轻松处理时区与时间计算
  32. Python 系列(三十二) 面向对象编程入门,从零理解类、对象、封装与继承
  33. Python 系列(三十三) 什么是类和对象?零基础理解 Python class
  34. Python 系列(三十四) Python 属性详解,搞懂类属性、实例属性和 setter
  35. Python 系列(三十五) 学会特殊方法,让对象打印出你想要的内容
  36. Python 系列(三十六) 多个对象如何共享数据?彻底搞懂类变量
  37. Python 系列(三十七) 搞懂父类、子类和方法重写,真正理解继承与多态
  38. Python 系列(三十八) 如何判断数据类型?三个内置函数一次讲明白
  39. Python 系列(三十九) 搞懂异常模型与常见异常类型,读懂程序报错
  40. Python 系列(四十) 从零学会异常处理,掌握 try、except、else 和 finally
  41. Python 系列(四十一) ValueError、TypeError 到底是什么?一文搞懂常见异常
  42. Python 系列(四十二) ValueError、TypeError 到底是什么?一文搞懂常见异常
  43. Python 系列(四十三) 别只会处理异常!学会自定义异常,让程序更容易维护
  44. Python 系列(四十四) 从零认识 assert 断言,让程序学会检查自己
  45. Python 系列(四十五) 使用 sys 模块处理标准输入与输出
  46. Python 系列(四十六) 使用 open() 读取和写入文件
  47. Python 系列(四十七) 学会读写文本文件,让程序保存运行结果
  48. Python 系列(四十八) 搞懂 bytes、bytearray 和二进制读写
  49. Python 系列(四十九) 使用 seek() 和 tell() 灵活控制文件读取位置
  50. Python 系列(五十) 掌握 os 和 os.path,实现文件与目录自动化管理
  51. Python 系列(五十一) 掌握列表推导式,轻松实现列表生成与数据筛选
  52. Python 系列(五十二) 掌握字典推导式,让字典创建更简单
  53. Python 系列(五十三) 从零理解生成器,轻松处理大量数据
  54. Python 系列(五十五) 使用 zip() 同时遍历多个集合
  55. Python 系列(五十六) 使用 re 模块进行正则表达式匹配
  56. Python 系列(五十七) 理解正则表达式的字符类和量词
  57. Python 系列(五十八) 搞懂正则表达式中的分组与量词
  58. Python 系列(五十九) 学会正则捕获组,轻松提取文本数据
  59. Python 系列(六十) 掌握 re 模块的常用函数
  60. Python 系列(六十一) JSON 是什么?语法规则、数据类型与校验方法
  61. Python 系列(六十二) 使用 pdb 逐步调试程序,定位代码问题
  62. Python 系列(六十三) 在交互式 Python shell 中启动调试器
  63. Python 系列(六十四) 从零开始学习 pdb 命令行调试
  64. Python 系列(六十五) 使用 pdb 设置断点并检查程序执行上下文

在前面的文章中,我们已经认识了正则表达式,并学习了如何使用 Python 的 re 模块查找符合条件的文本。

例如,我们想从一段服务器日志中提取数字:

1
2
3
4
5
6
7
import re

text = "服务器 web01 的 CPU 使用率为 85%"

result = re.search(r"\d+", text)

print(result.group())

运行结果:

1
01

为什么我们想查找 CPU 使用率,程序却返回了 01,而不是 85?

这是因为 re.search() 会从字符串的开头开始查找,找到第一个符合正则表达式规则的内容后,就会立即返回。

在这个例子中,\d+ 表示匹配一个或多个连续的数字字符。字符串中的 web01 最先出现数字,因此程序首先匹配到了 01。

如果我们希望找到 CPU 使用率中的数字,就需要进一步限定匹配条件,例如匹配 CPU= 后面的数字。

这正是本文要学习的内容:使用字符类规定字符需要满足什么条件,再使用量词规定匹配项可以出现多少次。

认识正则表达式中的字符类和量词

在学习具体语法之前,我们先明确几个容易混淆的概念。

概念作用示例
普通字符匹配字符自身a、5、%
字符类规定单个字符可以满足什么条件[abc]、[a-z]
预定义字符类使用简写表示一类字符\d、\w、\s
量词规定前面的匹配项可以出现多少次*、+、?、{m,n}
分组将表达式的一部分组合起来,并可捕获匹配内容(\d+)
匹配函数决定如何查找或验证匹配结果search()、findall()、fullmatch()

初学阶段,可以先记住四句话:

  • 字符类规定一个字符需要满足什么条件。
  • 量词规定前面的匹配项可以出现多少次。
  • 分组用于组织表达式,并可以捕获匹配内容。
  • 匹配函数负责按照正则表达式查找或验证文本。

例如,下面这个表达式:

1
r"\d+"

包含两个部分:

  • \d 是预定义字符类的简写,表示匹配一个数字字符。
  • + 是量词,表示前面的匹配项至少出现一次。

组合起来,\d+ 就表示匹配一个或多个连续的数字字符。

需要注意,量词修饰的不一定只是一个普通字符。它可以修饰普通字符、字符类、预定义字符类,也可以修饰分组。

使用预定义字符类匹配常见字符

正则表达式提供了一些常用的简写规则,可以帮助我们快速描述数字、单词字符和空白字符。

使用 \d 匹配数字

\d 是预定义字符类的简写,用于匹配一个数字字符。

例如:

1
2
3
4
5
6
7
import re

text = "服务器 CPU 使用率为 85%"

result = re.search(r"\d", text)

print(result.group())

运行结果:

1
8

为什么输出的是 8,而不是 85?

因为 \d 只匹配一个数字字符。

程序从左向右查找,遇到数字 8 后,就已经完成了一次匹配。后面的 5 不属于这次匹配的内容。

如果想匹配两个连续的数字,可以写成:

1
2
3
4
5
6
7
import re

text = "服务器编号为 123"

result = re.search(r"\d\d", text)

print(result.group())

运行结果:

1
12

这里的 \d\d 表示两个连续的数字字符。

但如果服务器编号长度不固定,例如有 01、123、2026,每次都重复书写 \d 就不方便了。

这时,我们就可以使用量词。

使用 \w 匹配单词字符

\w 是预定义字符类的简写,用于匹配一个单词字符。

在 Python 默认的 Unicode 模式下,它通常包括:

  • 英文字母,例如 a、Z。
  • 数字,例如 0、9。
  • 下划线 _。
  • 许多 Unicode 字母和数字,包括常见的汉字。

例如:

1
2
3
4
5
6
7
import re

text = "web_01"

result = re.search(r"\w+", text)

print(result.group())

运行结果:

1
web_01

这里的 \w+ 能够匹配整个 web_01,因为字母、数字和下划线都属于 \w 可以匹配的字符。

需要注意,\w 并不等于“只匹配英文字母”。如果只想匹配英文字母,可以使用 [a-zA-Z]。

使用 \s 匹配空白字符

\s 是预定义字符类的简写,用于匹配一个空白字符,例如:

  • 普通空格。
  • 制表符 \t。
  • 换行符 \n。

例如:

1
2
3
4
5
6
7
import re

text = "web01 CPU"

result = re.search(r"\s", text)

print(repr(result.group()))

运行结果:

1
' '

这里使用 repr(),是为了让空格以更容易观察的形式显示出来。

如果文本中存在多个连续空格,单独使用 \s 仍然只匹配其中一个空白字符。

后面学习量词后,就可以使用 \s+ 匹配一个或多个连续的空白字符。

使用方括号创建字符类

除了 \d、\w 和 \s 等预定义字符类,我们还可以使用方括号 [] 自己指定允许匹配的字符。

例如:

1
2
3
4
5
6
7
import re

text = "服务器状态:A"

result = re.search(r"[ABC]", text)

print(result.group())

运行结果:

1
A

[ABC] 表示匹配 A、B、C 中的任意一个字符。

假设字符串是:

1
text = "服务器状态:B"

那么匹配结果就是 B。

如果字符串是:

1
text = "服务器状态:D"

那么 [ABC] 就无法匹配到任何内容。

这里有一个重要规则:方括号中的多个普通字符通常表示任选其中一个,而不是要求这些字符按顺序全部出现。

例如:

字符类含义
[ABC]匹配 A、B、C 中的任意一个
[0123456789]匹配一个 ASCII 数字
[a-z]匹配一个小写英文字母
[A-Z]匹配一个大写英文字母
[a-zA-Z]匹配一个大小写英文字母
[a-zA-Z0-9]匹配一个英文字母或 ASCII 数字

例如,提取服务器名称中的一个英文字母:

1
2
3
4
5
6
7
import re

text = "web01"

result = re.search(r"[a-zA-Z]", text)

print(result.group())

运行结果:

1
w

[a-zA-Z] 只匹配一个英文字母,因此结果是 w,而不是整个 web。

如果想匹配连续的多个英文字母,就需要使用量词。

使用 [^...] 匹配指定字符以外的内容

方括号中的 ^ 在特定位置具有特殊含义。

当 ^ 出现在字符类 [] 的开头时,表示取反,也就是匹配不属于指定字符集合的字符。

例如:

1
2
3
4
5
6
7
import re

text = "web01"

result = re.search(r"[^0-9]", text)

print(result.group())

运行结果:

1
w

[^0-9] 表示匹配一个不是 ASCII 数字 0 到 9 的字符。

由于字符串以 w 开头,而 w 不是数字,所以程序首先匹配到 w。

注意,^ 并不是在任何位置都表示取反:

  • [^0-9]:在字符类内部,位于开头的 ^ 表示取反。
  • ^abc:在字符类外部,^ 通常表示字符串开头。

理解这个区别,可以避免后续编写正则表达式时产生混淆。

使用量词规定匹配项的出现次数

前面学习了字符类,可以规定字符需要满足什么条件。

但在实际工作中,我们往往还需要规定匹配项出现多少次。

例如:

  • 服务器编号可能有两位、三位或四位数字。
  • CPU 使用率可能是 8%、85% 或 100%。
  • 日志中可能存在多个连续空格。
  • 服务器名称可能由多个英文字母和数字组成。

如果只能匹配一个字符,就无法方便地处理这些情况。

量词就是用来解决这个问题的。

量词用于限定它前面的匹配项可以出现多少次。

这个匹配项可以是普通字符、字符类、预定义字符类,也可以是分组。

使用 + 匹配一个或多个

+ 表示前面的匹配项至少出现一次,可以出现多次。

例如:

1
2
3
4
5
6
7
import re

text = "服务器 CPU 使用率为 85%"

result = re.search(r"\d+", text)

print(result.group())

运行结果:

1
85

这里的 \d+ 可以拆成两部分:

  • \d:匹配一个数字字符。
  • +:要求前面的匹配项至少出现一次。

因此,\d+ 可以匹配 8、85、100 等不同长度的连续数字。

再看一个例子:

1
2
3
4
5
6
7
import re

text = "web01 app2026 db03"

result = re.findall(r"\d+", text)

print(result)

运行结果:

1
['01', '2026', '03']

这里使用了 re.findall(),它会查找所有符合条件的内容,并以列表形式返回结果。

这说明了两个不同的概念:

  • 正则表达式 \d+ 决定匹配什么内容。
  • findall() 决定查找并返回所有匹配结果。

使用 * 匹配零个或多个

* 表示前面的匹配项可以出现零次、一次或者多次。

例如:

1
2
3
4
5
6
7
import re

text = "web01"

result = re.search(r"\d*", text)

print(repr(result.group()))

运行结果:

1
''

为什么结果是空字符串?

因为 * 允许前面的匹配项出现零次。

字符串开头的 w 不是数字,但 \d* 可以匹配零个数字,因此它在字符串开头就完成了一次空匹配。

如果把表达式改成 \d+,就必须至少匹配一个数字,不能在开头通过零个数字完成匹配。

例如:

1
2
3
4
5
6
7
import re

text = "web01"

result = re.search(r"\d+", text)

print(result.group())

运行结果:

1
01

初学时需要特别注意:* 并不表示“必须匹配很多次”,而是表示“允许出现零次或多次”。

使用 ? 匹配零个或一个

? 表示前面的匹配项可以出现零次或一次。

例如,英文中有 color 和 colour 两种拼写。如果希望两种写法都能匹配,可以这样写:

1
2
3
4
5
6
7
8
9
import re

text1 = "color"
text2 = "colour"

pattern = r"colou?r"

print(re.search(pattern, text1).group())
print(re.search(pattern, text2).group())

运行结果:

1
2
color
colour

这里的 u? 表示字母 u 可以出现零次或一次。

因此,这个表达式既可以匹配不包含 u 的 color,也可以匹配包含 u 的 colour。

在处理日志时,也可以使用 ? 处理某些可选字符,例如可选连字符或可选空格。

使用 {m,n} 规定出现次数

有时,我们不希望匹配项出现任意次数,而是希望规定一个明确的范围。

例如,假设某个系统使用六位数字作为验证码,我们希望匹配六个连续的数字。

可以这样写:

1
2
3
4
5
6
7
import re

text = "验证码为 123456"

result = re.search(r"\d{6}", text)

print(result.group())

运行结果:

1
123456

这里的 {6} 表示前面的 \d 必须连续匹配六次。

除此之外,还可以使用以下形式:

量词含义
{3}恰好出现三次
{2,4}出现两到四次
{2,}至少出现两次
{0,3}出现零到三次

例如,假设服务器编号由两到四位数字组成:

1
2
3
4
5
6
7
import re

text = "服务器编号:1234"

result = re.search(r"\d{2,4}", text)

print(result.group())

运行结果:

1
1234

这里的 \d{2,4} 表示匹配两到四个连续的数字。

不过要注意,re.search() 只要求找到一个符合模式的子串,并不会自动检查整个字段是否完全符合要求。

例如:

1
2
3
4
5
import re

result = re.search(r"\d{2,4}", "12345")

print(result.group())

运行结果:

1
1234

虽然字符串有五位数字,但正则表达式仍然能够从中找到四位数字作为匹配结果。

如果要求整个字符串必须恰好是两到四位数字,可以使用 re.fullmatch():

1
2
3
4
import re

print(re.fullmatch(r"\d{2,4}", "1234") is not None)
print(re.fullmatch(r"\d{2,4}", "12345") is not None)

运行结果:

1
2
True
False

第一个字符串符合规则,第二个字符串不符合规则。

将字符类和量词组合起来使用

前面分别学习了字符类和量词。实际编写正则表达式时,我们通常会把它们组合起来。

例如:

正则表达式含义
\d+一个或多个连续数字
[a-z]+一个或多个连续小写英文字母
[A-Z]{2,4}两到四个连续大写英文字母
\w+一个或多个连续单词字符
\s+一个或多个连续空白字符

这些表达式的共同点是:先确定匹配项的条件,再用量词规定重复次数。

匹配服务器名称

假设我们有一个简单的服务器清单:

1
2
3
4
5
6
7
import re

text = "web01 app2026 db03"

result = re.findall(r"[a-z]+\d+", text)

print(result)

运行结果:

1
['web01', 'app2026', 'db03']

这里的正则表达式可以拆成两部分:

  • [a-z]+:匹配一个或多个连续的小写英文字母。
  • \d+:匹配一个或多个连续的数字字符。

组合起来,便可以匹配先出现英文字母、再出现数字的字符串。

例如,web01 符合规则,app2026 也符合规则。

这里需要区分“提取子串”和“验证完整字段”。

findall() 会从文本中提取符合模式的内容。如果希望验证某个完整的服务器名称是否符合规则,可以使用 fullmatch():

1
2
3
4
5
6
import re

pattern = r"[a-z]+\d+"

print(re.fullmatch(pattern, "web01") is not None)
print(re.fullmatch(pattern, "web01-prod") is not None)

运行结果:

1
2
True
False

web01 完全符合规则,而 web01-prod 包含连字符,不能通过当前模式的完整匹配。

提取日志中的 CPU 使用率

假设日志内容如下:

1
2
3
4
5
6
7
8
9
import re

log = "2026-10-22 14:00:00 web01 CPU=85%"

result = re.search(r"CPU=(\d+)%", log)

if result:
print(result.group())
print(result.group(1))

运行结果:

1
2
CPU=85%
85

这里出现了一个新的写法:(\d+)。

圆括号可以将表达式的一部分组合起来,并捕获这部分匹配内容。这里的 group(1) 获取第一组,也就是数字 85。

从术语上看:

  • \d 是预定义字符类。
  • + 是量词,修饰前面的 \d。
  • (\d+) 是一个捕获分组。
  • CPU=(\d+)% 是完整的正则表达式模式。

因此,量词和分组并不是同一类概念。分组将一部分表达式组合起来,而量词可以修饰整个分组。

例如:

1
2
3
4
5
6
7
import re

text = "ababab"

result = re.search(r"(ab)+", text)

print(result.group())

运行结果:

1
ababab

这里的 (ab)+ 表示整个 ab 组合可以连续出现一次或多次,而不是只要求字母 b 重复出现。

分组的详细用法将在下一篇文章中介绍。

匹配连续空白字符

假设我们要处理一份格式不太整齐的文本:

1
2
3
4
5
6
7
import re

text = "web01 CPU=85%"

result = re.search(r"\s+", text)

print(repr(result.group()))

运行结果:

1
'     '

这里的 \s+ 表示匹配一个或多个连续空白字符。

由于服务器名称与 CPU 信息之间存在五个空格,所以程序匹配到了这五个空格。

在日志清洗中,我们还可以利用 re.sub() 将连续空白替换成一个普通空格:

1
2
3
4
5
6
7
import re

text = "web01 CPU=85%"

result = re.sub(r"\s+", " ", text)

print(result)

运行结果:

1
web01 CPU=85%

re.sub() 的作用是替换符合正则表达式的内容。这里把一个或多个连续空白字符替换成一个空格,让文本格式更加整齐。

理解量词的贪婪匹配

学习量词时,还有一个值得理解的概念:贪婪匹配。

默认情况下,*、+ 和 {m,n} 等量词通常采用贪婪匹配行为,也就是在不影响整个表达式匹配成功的前提下,尽可能多地匹配。

例如:

1
2
3
4
5
6
7
import re

text = "12345"

result = re.search(r"\d{2,4}", text)

print(result.group())

运行结果:

1
1234

{2,4} 允许前面的数字匹配两到四次。在这个例子中,正则表达式可以匹配四位数字,因此默认优先得到 1234。

如果希望量词尽可能少地匹配,可以在量词后面添加 ?,形成非贪婪量词:

1
2
3
4
5
6
7
import re

text = "12345"

result = re.search(r"\d{2,4}?", text)

print(result.group())

运行结果:

1
12

这里的 {2,4}? 表示匹配两到四位数字,但优先尝试匹配最少的两位数字。

需要注意,非贪婪匹配并不意味着永远只匹配最少字符。如果后续的匹配条件要求更多字符,正则表达式仍可能扩大匹配范围。

另外,贪婪和非贪婪描述的是量词的匹配行为,不是与字符类并列的一种字符类型。

字符类和量词的匹配流程

现在,我们已经认识了字符类、量词,以及它们的组合用法。可以通过下面的流程图理解正则表达式查找匹配内容时的大致过程。

flowchart TD
    A["准备待匹配的字符串"] --> B["编写正则表达式模式"]
    B --> C["从字符串的某个位置开始尝试"]
    C --> D{"当前字符满足匹配项条件吗?"}
    D -- "不满足" --> E["尝试其他位置或判定失败"]
    D -- "满足" --> F["根据量词检查重复次数"]
    F --> G{"整个模式能否匹配成功?"}
    G -- "能" --> H["返回匹配结果"]
    G -- "不能" --> I["尝试调整匹配范围"]
    I --> J{"还有其他尝试方式吗?"}
    J -- "有" --> C
    J -- "没有" --> K["没有找到匹配结果"]

可以把这个过程简单理解为:

  • 字符类规定字符条件。 例如,\d 匹配数字,[a-z] 匹配小写英文字母。
  • 量词规定重复次数。 例如,+ 表示至少一次,* 表示零次或多次,{3} 表示恰好三次。
  • 完整模式组合匹配规则。 例如,[a-z]+\d+ 要求先匹配一个或多个小写英文字母,再匹配一个或多个数字。
  • 匹配函数决定查找方式。 例如,search() 返回第一个匹配结果,findall() 返回所有匹配结果,fullmatch() 要求整个字符串符合模式。

需要说明的是,流程图展示的是便于初学者理解的简化过程。实际正则表达式引擎还会根据量词、分组、分支等规则进行回溯和其他匹配处理。

常见问题

\d 和 [0-9] 有什么区别?

在 Python 默认的 Unicode 模式下,\d 可以匹配许多 Unicode 十进制数字字符,而 [0-9] 只匹配 ASCII 范围内的数字 0 到 9。

例如,处理标准服务器编号、监控数据或验证码时,如果只希望接受普通的 ASCII 数字,可以使用 [0-9]+。

* 和 + 有什么区别?

两者最大的区别是能否匹配零个字符:

  • \d*:允许没有数字。
  • \d+:至少需要一个数字。

在查找数字时,如果不希望通过空匹配得到空字符串,通常可以考虑使用 \d+。

为什么 \d+ 没有匹配整个字符串?

因为 \d+ 只描述数字匹配规则,并不要求整个字符串都由数字组成。

例如:

1
2
3
4
5
6
import re

text = "web01"

print(re.search(r"\d+", text).group())
print(re.fullmatch(r"\d+", text))

运行结果:

1
2
01
None

search() 能在 web01 中找到数字 01,但 fullmatch() 要求整个字符串都是数字,因此返回 None。

为什么 \d{6} 仍然可能匹配更长的数字?

因为 \d{6} 只要求匹配六个连续的数字,并不限制前后不能再出现数字。

例如:

1
2
3
4
5
6
7
import re

text = "1234567"

result = re.search(r"\d{6}", text)

print(result.group())

运行结果:

1
123456

如果要求字符串必须恰好包含六位数字,可以使用:

1
2
3
4
import re

print(re.fullmatch(r"\d{6}", "123456") is not None)
print(re.fullmatch(r"\d{6}", "1234567") is not None)

运行结果:

1
2
True
False

这再次说明,编写正则表达式时,不仅要考虑字符条件和重复次数,还要明确自己究竟是要提取一段内容,还是验证整个字符串。

动手练习

练习一:提取所有数字

给定字符串:

1
text = "web01 app2026 db03"

要求提取其中所有连续的数字,预期结果:

1
['01', '2026', '03']

提示:使用 re.findall() 和 \d+。

练习二:匹配三到五位数字

给定字符串:

1
text = "12345"

要求匹配三到五位连续数字,预期结果:

1
12345

提示:可以使用 {m,n} 量词。如果要求整个字符串都符合规则,可以使用 re.fullmatch()。

练习三:提取服务器名称

给定字符串:

1
text = "web01 app2026 db03"

要求提取由小写英文字母和数字组成、且先出现字母再出现数字的服务器名称,预期结果:

1
['web01', 'app2026', 'db03']

提示:尝试组合 [a-z]+ 和 \d+。

写在最后

本文学习了正则表达式中的字符类和量词。

字符类用于规定单个字符需要满足什么条件,例如 [a-zA-Z];\d、\w 和 \s 则是常用的预定义字符类简写。

量词用于限定前面的匹配项可以出现多少次,例如 + 表示一个或多个,* 表示零个或多个,? 表示零个或一个,{m,n} 表示指定次数范围。

将字符类与量词组合起来,就可以完成数字提取、服务器名称匹配、日志清洗等常见任务。

下一篇,我们将继续学习正则表达式中的分组功能,了解如何使用圆括号 () 组织表达式、捕获匹配内容,并通过 group() 获取自己需要的数据。