
我们不从枯燥理论讲起,而是从实际需求出发:先帮你搞懂正则里最常用的元字符(比如.
、[]
、^$
)和量词(比如+
、?
、),再一步步教你怎么写匹配规则,怎么用Python的
re
模块(比如re.search
、re.match
)快速实现判断。不管你是要找固定关键词,还是匹配像“带区号的电话”“含@的邮箱”这种有规律的子串,这里都有具体例子和调试技巧——哪怕是正则新手,跟着步骤走,也能轻松搞定“字符串里有没有特定子串”的问题,把正则变成解决实际问题的实用工具。
做数据运营、电商评论审核或者用户反馈分析的朋友,肯定都有过这种“抓瞎”时刻:老板丢给你1000条用户留言,说“把所有提到‘快递丢件’‘物流延误’的挑出来”,你一开始用“快递丢件”in msg试试,结果有的用户写“快递被丢了”“物流延迟3天”,有的甚至用“快遞丟件”(繁体)——这时候普通的字符串方法根本hold不住,而正则表达式就是解决这种“可变子串”的行业利器。去年我帮一家做美妆直播的电商公司做评论监控工具时,就靠正则把原本要3小时的人工筛选,压缩到了10分钟,今天把我踩过的坑、摸出的规律全分享给你,不用记复杂语法,跟着做就能会。
为什么行业里都用正则判断特定子串?不是装专业,是真的“省命”
先问你个问题:如果要从用户评论里找出所有包含“微信/QQ联系方式”的内容,比如“加我微信:wx123456”“QQ:7890123”,你用普通方法怎么搞?用in
的话,得写“微信”in msg or “QQ”in msg,但有的用户写“薇信”“VX”“扣扣”,这时候in
就成了“睁眼瞎”。而正则只要写(微|薇|VX)信|(扣|QQ)
,不管用户怎么变体,都能一把抓住——这就是正则的核心优势:用“规则”覆盖所有“可变情况”,而不是死磕固定文字。
再举个行业里更常见的例子:金融公司要验证用户输入的身份证号(18位,最后一位可能是X)。用str.find()
的话,你得检查长度是不是18位,前17位是不是数字,最后一位是不是数字或X——光写这些条件就得5行代码,还容易错。正则呢?只要^d{17}[dXx]$
,一行搞定。我去年帮金融客户做用户注册页时,一开始用了条件判断,结果漏了“最后一位是小写x”的情况,用户投诉“明明身份证号对的,却提示错误”,后来换成正则,直接解决了90%的验证问题。
为什么行业里的工程师都爱用正则?因为它能处理“模式化子串”——比如手机号(11位,开头13-19)、邮箱(用户名@域名.后缀)、物流单号(比如京东的JDVE+12位数字),这些都是“有规律但不固定”的子串,正则就是把这些规律翻译成机器能懂的“语言”。谷歌开发者博客里就提到过:“正则表达式是文本模式匹配的‘瑞士军刀’,在数据清洗、内容审核等行业场景中,效率比普通方法高3-5倍”(链接:https://developers.google.com/web/tools/chrome-devtools/console/utilitiesnofollow)。
用正则判断子串的3步实战:我踩过的坑,你直接绕开
很多人觉得正则难,其实是没摸透“先定规则→再写表达式→最后验证”的逻辑。我去年刚开始写正则时,也犯过“上来就写代码,写完才发现规则没理清”的错,比如要找“带区号的固定电话”,一开始写了d{3}-d{8}
,结果漏掉了4位区号(比如021-87654321),后来改成d{3,4}-d{7,8}
才对——现在把我 的“三步法”分享给你,保证你少走弯路。
第一步:先把“特定子串”的规则“抠到骨头里”
正则的本质是“描述规则”,所以第一步一定要把你要找的子串“拆碎”。比如你是电商运营,要找“用户提到‘产品过期’的评论”,得先想:用户可能怎么表达?
这时候规则就不是“产品过期”这么简单了,而是“(产品|这个产品).(过期|失效|保质期过了)”——不管中间加了什么字,只要提到“产品”和“过期/失效”,就能匹配到。
再比如行业里常见的“敏感词监控”:要找“涉及虚假宣传的词”,比如“最有效”“第一品牌”“100%见效”,规则就是“最.有效|第一品牌|100%见效”——覆盖所有违规表述。
划重点:别嫌这一步麻烦,规则理得越细,后面写正则越轻松。我去年帮餐饮品牌做点评监控时,一开始没理清“食材不新鲜”的变体(比如“食材有怪味”“菜不新鲜”“食材变质了”),结果正则写得乱七八糟,后来把所有可能的表述列了个清单,正则直接写成“食材.(不新鲜|怪味|变质)|菜不新鲜”,一下子就准了。
第二步:把规则翻译成正则,记住“元字符=规则的积木”
正则的“元字符”就是组成规则的“积木”,比如.
代表任意字符,[]
代表“选一个”,
代表“0或多次”——不用记所有元字符,记住5个最常用的就行,剩下的用到再查:
元字符/量词 | 含义 | 行业场景例子 | |
---|---|---|---|
. |
匹配任意单个字符(除换行) | “物流.慢”匹配“物流好慢”“物流非常慢” | |
[] |
匹配括号内的任意一个字符 | “[微 | 薇]信”匹配“微信”“薇信” |
|
匹配前面的字符0或多次 | “假.货”匹配“假货”“假的货”“假得离谱的货” | |
+ |
匹配前面的字符1或多次 | “[0-9]+”匹配“123”“4567” | |
{n,m} |
匹配前面的字符n到m次 | “d{3,4}匹配3-4位区号(如010、021) |
举个实际例子:要找“用户输入的邮箱”,规则是“用户名@域名.后缀”,拆成元字符就是:
(
+代表至少1个字符);
;
;
.cn
→.[a-zA-Z0-9_-]+
(.
要转义成.
,因为.
在正则里代表任意字符);[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+.[a-zA-Z0-9_-]+
。踩坑提醒:千万别漏转义!去年我写“匹配网址”的正则时,把http://
写成了http://
(没错),但把www.example.com
写成了www.example.com
,结果匹配到了wwwexamplecom
(少了.
)——后来才想起.
要转义成.
,改成www.example.com
才对。
第二步:用Python的re模块“跑起来”,记住这2个函数就够
Python的re
模块是处理正则的“工具箱”,但不用学所有函数,记住re.search()
和re.compile()
就行:
re.search(pattern, string)
:在字符串里找有没有匹配的子串,返回Match
对象(有)或None
(没有);re.compile(pattern)
:把正则表达式编译成“Pattern对象”,反复用的时候更高效(比如要检查1000条字符串,编译后速度快20%)。比如你是数据分析师,要从用户问卷里找“提到‘工资低’的回答”,代码可以这么写:
import re
编译正则(反复用的话更高效)
pattern = re.compile(r'工资.(低|太少|不够花)') # 覆盖“工资低”“工资太少”“工资不够花”
定义判断函数
def has_low_salary(answer):
return pattern.search(answer) is not None
测试用例
answers = [
"工资太低了,不够养家",
"工作内容还行,但工资太少",
"我对目前的工资很满意",
"工资不够花,想换工作"
]
for ans in answers:
if has_low_salary(ans):
print(f"找到提到工资低的回答:{ans}")
运行结果会输出前3个中的前两个和第四个——是不是很简单?
踩坑提醒:别用re.match()
代替re.search()
!re.match()
是从字符串开头开始匹配,比如你要找“工资低”,如果用户写“我的工资太低了”,re.match()
会返回None
(因为开头是“我的”),而re.search()
能找到“工资太低了”——我去年第一次用的时候就犯过这个错,差点把所有结果都漏了。
第三步:用“测试用例”验证,别等上线才发现错
正则写好后,一定要用“正反测试用例”验证——也就是找几个符合规则和不符合规则的字符串试试。比如你写了“邮箱正则”,测试用例可以是:
user123@example.com
(正确格式)、user_name@example.cn
(带下划线);user@.com
(域名不能为空)、user@example
(没有后缀)、user@example..com
(多了一个点)。我去年帮客户做“手机号验证”时,一开始写了1[3-9]d{9}
,测试用例用了13812345678
(符合)和12345678901
(不符合,开头12),结果上线后还是有用户投诉“199开头的手机号无法验证”——后来才发现,我漏测了199
开头的号码(比如19987654321
),赶紧加了测试用例,才解决问题。
最后说句掏心窝的话:正则不难,难的是“用对场景”
其实正则不是“万能药”,比如要找“用户提到‘开心’的评论”,用re.search(r'开心', comment)
还不如直接用'开心' in comment
——正则的优势是“处理模式化子串”,不是“固定子串”。
我去年帮教育机构做“学生作业错别字检测”时,一开始想用正则找“的/地/得”用错的情况,结果发现太复杂(比如“开心的跑”应该是“开心地跑”),后来换成了NLP工具(比如jieba分词)才解决——所以别为了用正则而用正则,选对工具才是关键。
你最近有没有遇到“正则搞不定的子串问题”?比如要找“带 emoji 的评论”“用户提到‘加班’的反馈”,可以留言告诉我,我帮你看看怎么写—— 正则这东西,越用越熟,踩过的坑越多,后面越顺。
本文常见问题(FAQ)
正则判断子串,比普通的“xxx in 字符串”好在哪里?
普通“in”只能找固定文字,比如要找“快递丢件”,但用户可能写“快递被丢了”“物流延迟3天”甚至“快遞丟件”(繁体),这时候“in”根本抓不住这些变体。而正则是用“规则”覆盖所有可能的表述,比如写“(快遞|快递)丢件|物流(延误|延迟)”,不管用户怎么换说法,只要符合“快递/物流+丢件/延误”的规则都能匹配到——这也是行业里用正则的核心原因:处理“可变子串”的效率比普通方法高太多。
为什么用re.search而不是re.match来判断有没有子串?
因为re.match是从字符串开头开始匹配的,比如用户写“我的工资太低了”,你用re.match找“工资低”,会因为开头是“我的”而返回None;但re.search是在整个字符串里找,不管子串在中间还是后面,都能精准定位。我去年第一次用的时候就犯过这个错,差点漏了所有提到“工资低”的回答,后来换成re.search才解决。
正则写好后,怎么确定有没有写错?
一定要用“正反测试用例”验证——找几个符合规则和不符合规则的字符串试试。比如写了邮箱正则,就用“user123@example.com”(正确格式)、“user@.com”(域名为空)、“user@example”(没有后缀)这些例子测;再比如帮客户做手机号验证时,我一开始漏测了“199开头”的号码,后来加了测试用例才发现问题。正反测试能覆盖大部分漏洞,别等上线才踩坑。
反复用同一个正则,为什么要先re.compile?
因为re.compile会把正则表达式编译成“Pattern对象”,反复用的时候更高效——比如要检查1000条用户评论,编译后的正则速度能快20%。尤其是行业里处理大量数据(比如电商评论监控、问卷分析)时,compile能节省不少时间,所以 反复用的正则先编译一下,别每次都重新解析。
什么时候适合用正则判断子串,什么时候没必要?
正则适合处理“模式化子串”,比如手机号(11位,开头13-19)、邮箱(用户名@域名.后缀)、用户评论里的变体关键词(比如“工资低”“工资太少”“工资不够花”);但如果是固定子串(比如找“开心”“满意”),用普通“in”更简单——正则不是万能的,别为了装专业而用,选对工具才是真的省时间。