当前位置:首页>python>Python 正则表达式中 match 和 search 有什么区别?

Python 正则表达式中 match 和 search 有什么区别?

  • 2026-09-09 17:56:55
Python 正则表达式中 match 和 search 有什么区别?

线上有个日志清洗脚本,明明日志里有订单号,正则也没写错,结果一跑全是空。

我第一眼不太信正则错了,先把代码扒出来看了一眼:

import re

line = "2026-06-04 10:21:33 INFO pay success, order_id=PO202606040001, cost=36ms"

m = re.match(r"order_id=(PO\d+)", line)

print(m.group(1) if m else"not found")

输出:

not found

这个地方就很典型。

不是 order_id 不存在,也不是 \d+ 写错了,是 match() 根本不是这么用的。

match() 只从字符串开头开始匹配。

它的意思不是“帮我在这行文本里找一下”,而是“从第一个字符开始,你能不能匹配上”。

上面这行日志第一个字符是 2,而正则第一个要求是 order_id=,那肯定匹配不上。

换成 search():

import re

line = "2026-06-04 10:21:33 INFO pay success, order_id=PO202606040001, cost=36ms"

m = re.search(r"order_id=(PO\d+)", line)

print(m.group(1) if m else"not found")

输出:

PO202606040001

这才是大多数日志处理脚本里真正想要的效果。

search() 会从字符串里往后扫,只要某个位置能匹配上,就返回第一个匹配结果。

所以这俩区别其实就一句话:

match() 看开头。

search() 看整段。

但这句话太容易听完就忘,真正踩坑一般都在业务代码里。

比如有个接口参数校验,要求用户输入的活动码必须是 ACT- 开头,后面跟 6 位数字。

这种场景我会用 match(),因为我本来就只认开头。

import re

defcheck_campaign_code(code: str) -> bool:
ifnot code:
returnFalse

    hit = re.match(r"ACT-\d{6}", code)
return hit isnotNone


print(check_campaign_code("ACT-102938"))       # True
print(check_campaign_code("xx ACT-102938"))    # False

这个结果没问题。

但是这里有个坑,我以前也见过有人这么写:

import re

defcheck_campaign_code(code: str) -> bool:
return re.match(r"ACT-\d{6}", code) isnotNone


print(check_campaign_code("ACT-102938-debug"))

输出是:

True

这就有点坑了。

因为 match() 只是要求从开头能匹配一段,不要求整串都匹配完。

ACT-102938-debug 的前面确实满足 ACT-\d{6},所以它返回 True。

如果你的意思是“整个字符串必须就是活动码”,别靠 match() 硬撑,直接用 fullmatch()。

import re

defcheck_campaign_code(code: str) -> bool:
return re.fullmatch(r"ACT-\d{6}", code or"") isnotNone


samples = [
"ACT-102938",
"ACT-102938-debug",
"xx ACT-102938",
]

for item in samples:
    print(item, check_campaign_code(item))

输出:

ACT-102938 True
ACT-102938-debug False
xx ACT-102938 False

这三个函数放一起看就清楚了。

match():从开头匹配一段。

search():在字符串任意位置找一段。

fullmatch():整条字符串必须全部匹配。

我平时写脚本一般是这么分的。

处理日志、HTML 片段、接口返回文本,用 search() 多一些,因为目标字段通常藏在中间。

校验前缀格式,用 match()。

校验整个字段是否合法,用 fullmatch()。

再看一个更像现场的例子。

有个批量导入用户的 CSV,手机号字段有时候被运营塞成这样:

张三,手机号: 13800138000,北京
李四,phone=13900139000,上海
王五,无手机号,广州

这个时候你要提取手机号,用 match() 就很别扭,因为手机号不在开头。

import re

rows = [
"张三,手机号: 13800138000,北京",
"李四,phone=13900139000,上海",
"王五,无手机号,广州",
]

phone_re = re.compile(r"1[3-9]\d{9}")

for row in rows:
    hit = phone_re.search(row)
    phone = hit.group(0) if hit else""
    print(row, "=>", phone)

输出:

张三,手机号: 13800138000,北京 => 13800138000
李四,phone=13900139000,上海 => 13900139000
王五,无手机号,广州 => 

这类东西别纠结,直接 search()。

但反过来,如果你在处理网关日志,只想找以 ERROR 开头的行,那 match() 就合适。

import re

lines = [
"ERROR timeout when call user-service",
"2026-06-04 ERROR timeout when call pay-service",
"WARN retry user-service",
]

err_head = re.compile(r"ERROR\s+")

for line in lines:
if err_head.match(line):
        print("报警:", line)

这里只会打印第一行。

第二行虽然也有 ERROR,但它不是开头。

这就是 match() 的价值,它不是弱版 search(),它只是更挑位置。

还有一个容易绕晕的地方:正则里的 ^。

如果你这么写:

re.search(r"^ERROR", line)

它看起来用了 search(),但因为正则里加了 ^,所以也只能匹配开头。

也就是说,在很多单行文本里:

re.match(r"ERROR", line)
re.search(r"^ERROR", line)

效果差不多。

但我一般不建议把代码写得太绕。

要表达“开头匹配”,就用 match()。

要表达“到处找”,就用 search()。

要表达“整串必须合法”,就用 fullmatch()。

代码是给后面排查问题的人看的,不是给自己炫技的。

最后再贴一个我自己常用的小写法,别每次都 if m is not None 写一坨。

import re

_order_id = re.compile(r"order_id=(PO\d{12,})")

defpick_order_id(raw: str) -> str:
    hit = _order_id.search(raw or"")
ifnot hit:
return""

return hit.group(1)


line = "INFO callback ok uid=88 order_id=PO202606040001 cost=21ms"
print(pick_order_id(line))

这种代码放在日志清洗、告警归因、批量补数据脚本里都够用。

记住一点就行:你想从第一个字符开始验,就 match();你想在一整段文本里捞东西,就 search()。

很多正则问题,坏就坏在函数选错了,不是表达式写错了。

最新文章

随机文章