R

ReflexKR

Reflex 官方文档

看得见的正则表达式

·

课程概览

1

消失的支付记录:按原样查找文字

2

肇事逃逸车辆:字符类

3

撕破的电话簿:量词

4

虚假注册:锚点与校验

5

五花八门的日期:分组与选择

6

凌晨 3 点的访问:命名分组与 Match 对象

7

见报前夜:替换与分割

8

回溯者的挑战书:贪婪、懒惰与占有

9

保险箱的规则:前后查找

10

同样的手法:反向引用与条件模式

11

读不懂的正则:标志与编译

12

回溯者的真面目:总复习与分词器

看得见的正则表达式
第1课

时间 0:00–0:50

消失的支付记录:按原样查找文字

CASE-001

Grep & Co. 数据侦探事务所

委托书

消失的支付记录

委托人

“升级”网吧老板

证物

支付服务器日志。一位顾客坚称自己付了 4.99 美元,可系统里没有记录。

破案条件

在日志里找出出错的行,并且只挑出金额正好是 $4.99 的支付。

上班第一天早上,所长放下咖啡杯,把一张纸推到你面前。“昨天网吧老板来过。顾客说付了 4.99 美元,记录却没有。日志不会说谎。去找。”

日志长这样。今天就从这五行里找证据。

2026-10-20 09:12:01 INFO  PAY user=kim  amount=$4.99  status=OK
2026-10-20 09:12:07 INFO  PAY user=lee  amount=$14.99 status=OK
2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99  status=TIMEOUT
2026-10-20 09:15:02 INFO  PAY user=choi amount=$4999  status=OK
2026-10-20 09:16:30 ERROR DB  connection lost

1-1. 正则表达式就是“文字的模式”(5 分钟)

正则表达式(regular expression)是一门小小的语言,用来写下你想找的文字长什么样子。在 Python 里用 re 模块来使用它。最简单的正则就是文字本身:模式 ERROR 找的就是 ERROR 这几个字。

import re

log = "2026-10-20 09:13:44 ERROR PAY user=park"
m = re.search("ERROR", log)
print(m)          # <re.Match object; span=(20, 25), match='ERROR'>
  • re.search(模式, 字符串) 会从前往后找出模式在字符串里第一次出现的位置。
  • 找到了就返回 Match 对象,找不到就返回 None。

证据分析室

re.search(r"", text)

返回值

试着在 search 和 findall 之间切换。search 只找第一个,findall 全部找出来。

1-2. 找没找到、在哪儿找到:Match 对象(5 分钟)

Match 对象既是“找到了”的证据,也是一份报告,写明找到了什么、在哪儿找到的。

m = re.search("ERROR", log)
if m:                 # 找到为真,None 为假
    print(m.group())  # ERROR       (找到的文字)
    print(m.span())   # (20, 25)    (开始位置, 结束位置)
  • 像 if m: 这样直接判断真假。没找到时是 None,这时调用 m.group() 会抛出 AttributeError。
  • span() 的结束位置不包含在内。和 log[20:25] 正好是 "ERROR" 是同一个规则。
  • 证据分析室的“第一个匹配”一栏里,显示的就是这些值。

1-3. 14 个特殊字符(10 分钟)

大多数字符只匹配它自己。但下面这 14 个是在正则里有特殊含义的元字符。

.  ^  $  *  +  ?  {  }  [  ]  \  |  (  )

今天只学其中第一个:.。. 表示除换行符以外的任意一个字符。其余的,以后每节课拿出一个来用。

re.search("a.c", "abc")    # 匹配:b 落在 . 的位置
re.search("a.c", "a-c")    # 匹配:- 也算任意字符
re.search("a.c", "ac")     # None:. 一定要占掉一个字符
re.search("a.c", "a\nc")   # None:换行符不算 .

多特的提醒

事务所的桌子、椅子,连所长的键盘上面,我哪儿都能爬上去。只有一样翻不过去:窗户(换行符)。所以我的名字叫多特(.)。听说哪天有人帮我打开窗户,我就能跳过去了。

证据分析室

re.search(r"", text)

返回值

a.c 只有在 a 和 c 之间正好夹着一个字符时才会匹配。

1-4. 用反斜杠关掉特殊含义(10 分钟)

现在回到案子上。假设最上面那五行日志存放在字符串 pay_log 里。为了找 4.99 美元的支付,写成 4.99 会怎样?

re.findall("4.99", pay_log)   # ['4.99', '4.99', '4.99', '4999']  ← 四个?

本该是两个的结果变成了四个。$14.99 后半截的 4.99 被抓了进来;又因为 . 是“任意字符”,连 $4999 里的 4999 也混了进来。想把元字符按原样查找,就在它前面加反斜杠 \。\. 就是一个真正的句点。

$ 也是元字符。它的含义第 4 课再讲,现在只要记住“直接写的话它不会去找美元符号”。所以正确答案是 \$4\.99。

re.findall(r"\$4\.99", pay_log)   # ['$4.99', '$4.99']

证据分析室

re.search(r"", text)

返回值

从 4.99 开始,依次加上 \. 和 \$。破案条件全部变绿就成功了。

1-5. 为什么要写成 r"...":原始字符串(5 分钟)

上面我们在引号前加了 r,写成 r"\$4\.99"。这叫原始字符串(raw string)。

反斜杠在 Python 字符串里本身就有特殊含义:"\n" 是换行,"\t" 是制表符。所以如果用普通字符串写正则,Python 会先把反斜杠解释掉,re 收到的就成了另一个模式。

print(len("\b"))    # 1  ← Python 把 \b 变成了一个“退格”字符
print(len(r"\b"))   # 2  ← 反斜杠和 b,原样两个字符
  • r"..." 不碰反斜杠,原封不动地交给 re。正则表达式一律用原始字符串来写。
  • 从 Python 3.12 起,在普通字符串里写 "\d" 这类 Python 不认识的转义,会出现 SyntaxWarning。用原始字符串就不会有警告。

1-6. 按原样查找顾客输入的文字:re.escape(5 分钟)

网吧老板又说:“有位顾客的账号是 kim.99+,这个也帮我找找。”这种文字直接写进模式,. 和 + 会被当成元字符。要按原样查找别人输入的文字,就用 re.escape,它会在元字符前面自动加上反斜杠。

print(re.escape("kim.99+"))                        # kim\.99\+
re.search(re.escape("kim.99+"), "user=kim.99+")    # 匹配

破案(10 分钟)

总结一下:出错的行用 ERROR 找,金额正好 4.99 美元的支付用 \$4\.99 找。把两个条件叠在一起,就会发现顾客 park 的支付因为 TIMEOUT 错误而没有被记录下来。

for line in pay_log.splitlines():
    if re.search("ERROR", line) and re.search(r"\$4\.99", line):
        print(line)
# 2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99  status=TIMEOUT

结案报告 — 支付服务器在 09:13:44 因超时将该笔支付判为失败,顾客的屏幕上却显示为已完成。建议网吧老板为顾客退款。

练习

  1. 找出日志里所有 INFO 的行。一共有几行?
  2. \$1.\.99 会找到什么?在分析室里验证之前,先自己猜一猜。
  3. 先猜猜 re.escape("1+1=2? [yes]") 的结果,再亲手运行看看。

所长的笔记本

  • 大多数字符只匹配它自己。只有 14 个元字符 . ^ $ * + ? { } [ ] \ | ( ) 是特殊的。

  • . 是除换行符以外的任意一个字符。真正的句点写作 \.

  • re.search 返回 Match 或 None。用 if m: 判断,用 m.group()、m.span() 查看。

  • 正则一律用 r"..." 原始字符串来写。

  • 按原样查找别人输入的文字,用 re.escape。

← 上一课

课程概览

下一课 →

第2课 · 肇事逃逸车辆:字符类

订阅新课时和新课程通知

本课程新增课时或推出后续课程时,我们会发邮件通知你。

收集项目:电子邮箱 · 用途:本课程的新课时和上线通知 · 保存期限:直到你要求删除。不会用于其他用途,也不会提供给他人。回复任意通知邮件,我们会立即删除。

问答

关于本课有任何疑问都可以在这里留言。所有语言版本页面的提问会汇集在一起。

以纯文本显示,不支持链接和格式。

R

ReflexKR

动手学 Python 和 Reflex。所有示例均已在 Reflex 0.10.0 上实际运行验证。

ReflexKR 免费,没有横幅广告。如果对你有帮助,欢迎请我们喝杯咖啡。 ☕ 请我们喝杯咖啡

联盟链接与赞助说明
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex