# 我实际使用的 20 个正则表达式模式(在大规模删除其他 200 个之后)
💡 关键要点
- 我从正则表达式极简主义者到极简主义者的旅程
- 正则表达式差点让我们的 API 崩溃的那次
- 分析实际重要的内容
- 幸存的 20 个模式
我曾经为电子邮件验证写了一个847个字符的正则表达式。它耗费了我三个小时的时间,我再也无法找回来,里面包含嵌套的前瞻、字符类异常,以及足够多的反斜杠让我眼睛发涩。我对此感到非常自豪。我在团队的Slack中发布了它,并 smug 地说:“这处理了所有边缘情况。”
然后有人给我链接到了 RFC 5322。
对于那些不知道的人,RFC 5322 是官方的电子邮件地址规范。实际的、完整的正则表达式模式用于验证每一个技术上合法的电子邮件地址,长度超过6,000个字符。它包括像括号中的注释、带有转义字符的引用字符串,以及方括号中的域文字。技术上,根据该规范,`"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com` 是一个有效的电子邮件地址。
我盯着我的847个字符的模式。然后看着RFC。然后又回看我的模式。然后我做了任何合理开发者会做的事情:我用 `/.+@.+\..+/` 替换了它,然后继续我的生活。因为——没有人真的使用那些边缘情况。如果他们使用了,他们就值得承受一切问题。
那是五年前。从那时起,我写了数百个正则表达式模式。我调试了让高级开发者哭泣的正则表达式。我优化了导致生产环境减速的模式。在此过程中,我学到了一件关键的事情:大多数正则表达式模式都是你永远不会需要的垃圾。
我从正则表达式极简主义者到极简主义者的旅程
我以前像一些人收集邮票一样收集正则表达式模式。我有一个巨大的 `regex-library.js` 文件,里面包含了所有可以想象的模式。带区域 ID 的 IPv6 地址。带 Luhn 算法验证的信用卡号。处理每个生僻协议的URL。带有来自1930年代的区域号码验证的社会安全号码。
这个文件长达3200行。我坚信我正在构建一些有价值的东西——一个能够在每个项目中节省我时间的综合库。我甚至开始为它编写文档,附上示例和性能基准。
然后我换了工作。
在我的新公司,我试图将我心爱的正则库导入我们的代码库。高级架构师在代码审查时看了一眼,问了一个简单的问题:“你在过去六个月中实际使用过这些中的哪一个?”
我用高亮笔逐行查看这个文件。在200多个模式中,我可能只使用过15个。其余的都是“以防万一”的模式——为问题寻找解决方案的解决方案。是我写的,因为它们在智力上有趣,而不是因为它们解决了实际问题。
就在那时,我开始了伟大的正则清理。我查看了每个模式,问自己:“在生产中我需要过这个吗?不是'我可能有一天会需要它',而是我真的需要过吗?”如果答案是否定的,那就删除。没有怜悯。没有“但是如果”例外。
文件从3200行减少到400行。然后是200行。然后减少到大约100行,其中包含20个我实际上定期使用的模式。你知道吗?我从未想念其他180个模式。甚至一点也没有。
正则表达式差点让我们的 API 崩溃的那次
让我告诉你我用正则表达式引起的最糟糕的生产事件。我们有一个接受用户生成内容的API端点——基本上是一个笔记字段,用户可以在其中写下他们想要的任何内容。听起来简单,对吧?
但我们想要检测并自动链接文本中的URL。所以我写了我认为很聪明的正则模式,它可以匹配URL,同时避免误报。它有前瞻来检查有效的协议,字符类用于域名,选用的端口号、路径段、查询参数和片段标识符。它很美丽。它很全面。这是一个灾难性的错误。
在测试中模式工作得很好。我给它投入了各种URL,它完美地处理了它们。我在周五下午部署到生产时感到相当不错。(是的,我知道。绝不要在周五发布。我是以痛苦的方式学到这个教训的。)
不到一个小时,我们的API响应时间从50毫秒变成30秒。然后开始出现超时。我们的监控系统像圣诞树一样亮起。用户在抱怨。我的电话响个不停。情况很糟糕。
罪魁祸首?用户粘贴了一长串文本,里面恰好包含了触发我正则表达式的灾难性回溯的模式。正则引擎正在尝试每一种可能的匹配组合,而对于一个5,000字符的输入字符串,这意味着数十亿次尝试。每个请求都在100%的CPU核心上达到30多秒的峰值,然后超时。
我们立即回滚,我花了一整个周末重写那个模式。新版本更简单,不那么“聪明”,并对重复设置了明确限制。它并没有捕捉每一种可能的URL格式——它捕捉到99.9%的用户实际使用的URL。并且它的运行时是微秒而不是秒。
这个事件让我学到了一件关键的事情:正则的复杂性是一种负担,而不是资产。你的模式越复杂,它在生产中就越可能反咬你。处理常见情况的简单模式几乎总是优于处理所有边缘情况的复杂模式。
分析实际重要的内容
经过多年的正则编写和从错误中学习,我建立了一个简单的框架来决定哪些模式值得保留。这归结为三个标准:
频率: 我每月至少使用这个模式一次吗?如果没有,我可以在需要时谷歌一下。没有必要记住或维护少见用例的模式。 可靠性: 该模式在不同的正则引擎中是否一致有效?JavaScript、Python和Go都略有不同的正则实现。依赖复杂功能的模式可能不可移植。 性能: 该模式是否以线性时间运行,或者会触发灾难性回溯?我学会了对嵌套量词和重叠替代保持警惕。使用这些标准,大多数模式都不符合条件。用于解析带时区偏移和周数的ISO 8601日期的复杂正则表达式?未通过频率测试——我每年可能需要两次,而且当我需要时,可以查找。用于验证IBAN银行账户号码的模式?未通过可靠性测试——它太复杂,我不相信自己能维护它。用于匹配嵌套括号的递归模式?未通过性能测试——这是一个期待回溯噩梦的情况。
剩下的模式是简单、快速,并解决我定期遇到的问题。它们不是最有趣的模式。它们不是让你感觉聪明的模式。但它们才是真正重要的。
最佳正则模式是你可以在生产故障时,凌晨2点理解的模式,那时你正试图弄清楚用户输入为什么会破坏你的验证。
幸存的 20 个模式
以下是我实际使用的正则表达式模式的完整列表,按类别组织。这些是幸存者——通过在实际项目中的反复使用证明了其价值的模式。
| 模式 | 用例 | 频率 | 备注 |
|---|---|---|---|
/^\s+|\s+$/g |
修剪空格 | 每日 | 是的,我知道有 trim(),但这个在更多上下文中有效 |
/\s+/g |
标准化空格 | 每日 | 将多个空格替换为单个空格 |
/[^a-z0-9]/gi |
去除特殊字符 | 每周 | 用于生成 slug、用户名等。 |
/^[a-z0-9_-]{3,16}$/i |
用户名验证 | 每周 | 字母数字、下划线、连字符,3-16 个字符 |
/^.{8,}$/ |
密码长度 | 每周 | 至少 8 个字符,仅此而已 |
/.+@.+\..+/ |
电子邮件验证 | 每周 | 对99.9%的情况足够好 |
/^https?:\/\//i |
检查 URL 协议 | 每周 | 仅 http 或 https,无需花哨 |
/\d+/g |
提取数字 | 每日 | 简单而快速 |
/^\d+$/ |
验证数字输入 | 每周 | 仅数字,什么都没有 |
/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ |
日期格式 YYYY-MM-DD | 每月 | 仅格式检查,而不是验证 |
/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i |
十六进制颜色代码 | 每月 | 有或没有哈希 |
/\$\{([^}]+)\}/g |
模板变量 | 每月 | 匹配 ${variable} 模式 |
//g |
HTML 注释 | 每月 | 用于删除注释 |
/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/ |
IPv4 地址 | 每月 | 格式检查,而不是范围验证 |
/^[a-z0-9-]+$/i |
Slug 验证 | 每周 | 仅小写字母、数字、连字符 |
/\r?\n/g |
换行符 | 每周 | 处理 Unix 和 Windows |
/[<>]/g |
基本 C
Written by the Cod-AI Team Our editorial team specializes in software development and programming. We research, test, and write in-depth guides to help you work smarter with the right tools. Related Tools Related Articles Git Workflow for Teams: Branching Strategies That Work — cod-ai.com Clean Code: 10 Principles That Make You a Better Developer — cod-ai.com The Git Workflow That Actually Works for Solo DevelopersPut this into practice Try Our Free Tools → |