上一篇讲了库,其中标准库的 re 模块就是干这个的:正则表达式。
正则(regular expression / regex)= 一段描述”长什么样”的模式。 不是去搜一个固定的词,而是搜”所有符合这个长相的东西”——所有 IP、所有邮箱、所有 4 位数字的设备 ID。对安全分析师来说,这是从一堆日志里捞针的核心武器。
这正是上一篇配套工具里 type() 帮你确认类型之后,真正开始”按模式提取”的工具。
先看全景:用正则只需三步
import re # 1. 导入 re 模块
pattern = "\d+" # 2. 写一个模式(存成字符串)
re.findall(pattern, "h32rb17") # 3. 在目标字符串里找所有匹配 → ['32', '17']re.findall() 是最常用的函数:返回所有匹配,装进一个列表。 两个参数——第一个是模式,第二个是被搜索的字符串。
模式如果只是普通字符,就当普通搜索用:
re.findall("ts", "tsnow, tshah, bmoreno") # → ['ts', 'ts'] 只找字面的 "ts"真正的威力来自特殊符号。下面两张表是全部核心。
速查表 1:字符类符号(匹配”哪种字符”)
每个符号匹配单个特定类型的字符:
| 符号 | 匹配什么 | 例子 |
|---|---|---|
\w | 任意字母/数字/下划线(A-z, 0-9, _) | "ID_A17" → I,D,_,A,1,7 |
\d | 任意单个数字(0-9) | "ID_A17" → 1,7 |
\s | 任意空白(空格、Tab、换行) | "user 1" 里的空格 |
. | 任意字符(除了换行) | 几乎匹配一切 |
\. | 字面的句点 . | 反斜杠转义,只匹配真正的点号 |
\.这个转义很关键:.在正则里是”任意字符”的通配符。当你真的想匹配一个句点(比如 IP 地址里的点、邮箱里的点),必须写\.来”取消它的特殊含义”。这和普通字符串里的转义是一个道理。
re.findall("\w", "h32rb17") # 每个字符都是字母数字 → 7 个元素
re.findall("\d", "h32rb17") # 只要数字 → ['3','2','1','7'] 4 个元素速查表 2:数量符号(匹配”几个”)
放在字符/字符类后面,指定重复几次:
| 符号 | 含义 | 例子 |
|---|---|---|
+ | 1 个或多个连续 | \d+ 匹配 1、12、12345 |
* | 0 个、1 个或多个 | 包括”零次”,会匹配出空字符串 |
{n} | 恰好 n 个 | \d{4} 匹配 4 位连续数字(如 1234) |
{m,n} | m 到 n 个之间 | \d{1,3} 匹配 1、12 或 123 |
关键对比 + vs *:
re.findall("\d+", "h32rb17") # → ['32', '17'] 一串连续数字算一个
re.findall("\d*", "h32rb17") # → ['', '32', '', '', '17', ''] 零次也匹配,夹一堆空串实战里
+远比*常用——你几乎总是想要”至少一个”,而不是连空字符串都收进来。*那串空串往往是 bug 来源。
{n} 和 {m,n} 的精确控制:
re.findall("\d{2}", "h32rb17 k825t0m c2994eh") # → ['32','17','82','29','94'] 正好 2 位
re.findall("\d{1,3}", "h32rb17 k825t0m c2994eh") # → ['32','17','825','0','299','4'] 1~3 位一个必须懂的机制:Python 从左往右扫,不回头
正则匹配的过程是这样的:
Python 从左到右扫字符串。一旦某段开头符合模式的第一个字符,就继续往后比;模式匹配完成后,从这次匹配的下一个字符重新开始。
所以遇到连续三个数字 123,用 \d{2} 会:先匹配 12 → 然后从 3 重新开始(但 3 后面没数字了,不够 2 个,丢弃)。不会回头把 23 也算上。理解这个”不重叠、不回头”的机制,才能预判正则到底会捞出什么。
手把手:构造一个真实 pattern
正则的核心技能是把目标拆成小块,每块对应一个符号。
假设这是一行混合日志,每个员工有:员工 ID、用户名(后跟冒号)、当天登录次数、部门:
employee_logins_string = "1001 bmoreno: 12 Marketing 1002 tshah: 7 Human Resources 1003 sgilmore: 5 Finance"任务:只提取”用户名 + 登录次数”,不要 ID 和部门。
拆解你要找的东西:
| 想要的部分 | 长什么样 | 对应符号 |
|---|---|---|
| 用户名 | 若干字母 | \w+ |
| 冒号 | 一个 : | : |
| 空格 | 一个空格 | \s |
| 登录次数 | 若干数字 | \d+ |
拼起来:
import re
pattern = "\w+:\s\d+"
employee_logins_string = "1001 bmoreno: 12 Marketing 1002 tshah: 7 Human Resources 1003 sgilmore: 5 Finance"
print(re.findall(pattern, employee_logins_string))
# → ['bmoreno: 12', 'tshah: 7', 'sgilmore: 5']ID 和部门因为不符合”字母+冒号+空格+数字”这个长相,被自动排除了。这就是正则的威力:你描述长相,它负责筛。
⚠️ 正则有风险:模式写松了会捞出多余的东西,写紧了会漏掉想要的。所以永远要拿真实数据测一遍。在线工具 regex101.com 能实时高亮匹配,调试正则神器。
给 vibe-coder:正则是你最该会读的”AI 代码”
正则是 AI 最爱生成、你最该看得懂的东西之一。当 AI 给你一段 re.findall(r"...", log):
- 先看它想匹配什么 —— 把 pattern 对着上面两张表拆开,就知道它在捞 IP 还是邮箱还是别的
- 警惕过度匹配 —— AI 给的正则经常”看起来对,实际多捞/漏捞”。拿你的真实数据测,别直接信
- 安全场景常见用途:从日志提取可疑 特征、提取所有外联 IP、解析告警里的字段、做输入校验(防注入时验证格式)
但也别神化它:正则不擅长嵌套结构(HTML、JSON)。看到 AI 用正则解析 HTML,提醒它——那是 专门的库(如 bs4)的活。
一个经典提醒:“用正则解析 HTML”是程序员圈著名的反模式。结构化数据用结构化的解析器,正则只适合”扁平文本里找模式”。
一句话总结
正则 = 描述长相的模式,re.findall() 把所有符合的捞进列表。两类符号:字符类(\w \d \s . \. 管”哪种字符”)+ 数量(+ * {n} {m,n} 管”几个”)。构造时把目标拆成小块逐一对应,写完一定拿真实数据测。会读会写正则,你就能从一堆原始日志里,几行代码捞出真正关心的那部分——这是安全自动化里使用频率最高的技能之一。