匹配手机号需先预处理再校验:用re.sub(r'1', '', text)提取数字和+号,若以+86开头取后11位校验1[3-9]\d{9},否则直接校验11位;邮箱应分层验证,先粗筛再检查@数、域名点数及TLD长度。\d+ ↩

匹配手机号时为什么总漏掉带+86或空格的号码
国内手机号清洗最常踩的坑是只写 ^1[3-9]\d{9}$,这会直接丢掉带国际区号、空格、短横线、括号的合法输入,比如 +86 138 1234 5678 或 (010) 138-1234-5678(虽然座机不属手机号,但用户常混输)。真实场景中,要先统一预处理再校验。
建议分两步走:
- 用
re.sub(r'[^\d+]', '', text)提取所有数字和加号,得到类似+8613812345678的干净串 - 再判断:若以
+86开头,截取后 11 位并检查是否符合1[3-9]\d{9};若无+86,直接校验 11 位 - 注意:别用
^和$锚定整个原始字符串——用户可能在句子中混写,比如联系我:138-1234-5678,谢谢!
邮箱正则为什么一用就报错或匹配过宽
网上流传的“万能邮箱正则”如 [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} 看似合理,但实际有三个硬伤:它会匹配 test@.com、a@b.c(TLD 过短)、甚至 abc@def@g.com(多个 @)。
更稳妥的做法是分层控制:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 先用
re.search(r'[\w.%+-]+@[\w.-]+\.\w+', text)做粗筛(\w比[a-zA-Z0-9_]更简洁且兼容下划线) - 对提取出的候选串做二次验证:用
str.count('@') == 1排除多 @,用.split('@')[1].count('.') >= 1确保域名含点,再用len(domain.split('.')[-1]) >= 2拦截单字母 TLD - 避免用
re.fullmatch直接套长正则——调试困难,且 Python 的re不支持 RFC 5322 全量语法
清洗时如何保留原始上下文不破坏结构
直接 re.sub 替换容易把整段文字变成空或乱码,尤其当手机号/邮箱嵌在中文句子里。关键不是“删”,而是“标出并替换为占位符”。
- 用
re.sub(r'(1[3-9]\d{9})', r'[PHONE:\1]', text)把号码包进标记,后续可统一脱敏或导出 - 邮箱同理:
re.sub(r'([\w.%+-]+@[\w.-]+\.\w+)', r'[EMAIL:\1]', text) - 如果需彻底移除,务必加
flags=re.IGNORECASE防止大小写漏匹配,同时用re.sub(r'\s+', ' ', ...).strip()收尾清理多余空格 - 注意:别在循环里反复调用
re.sub处理同一文本——正则编译开销大,应提前用re.compile()缓存模式
为什么用 re.findall 提取后列表为空
常见原因是没处理换行或 Unicode 空格。例如用户粘贴的文本含全角空格(\u3000)、不间断空格(\u00a0),或邮箱前后有换行符,导致 @ 前后断开。
- 预处理时加一步:
text = re.sub(r'[\u3000\u00a0\s]+', ' ', text)统一空白符 - 手机号提取慎用
re.findall(r'1[3-9]\d{9}', text)——它会把138123456789(12 位)也截出前 11 位,应改用r'(? 加上左右负向断言 - 邮箱同理,用
r'(? 防止匹配到abc@test.comx中的test.com
真实清洗逻辑往往卡在边界 case:用户输入 “我的微信同手机号 138 1234 5678,邮箱是 abc@def.co.uk.” ——这里既有空格干扰,又有带点的国家码 TLD。正则只是工具,真正要花时间的是观察样本、分类 bad case、再反推模式约束。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










