如何在线测试正则表达式
正则表达式是编程中最强大的工具之一,也是最令人沮丧的之一。正则测试器让您可以交互式地构建和调试模式,而不是运行代码、检查输出和猜测出了什么问题。反馈循环从每次迭代几分钟降到几秒钟。
为什么使用正则测试器
在代码编辑器中编写正则表达式意味着您只能在运行时看到错误。测试器向您显示:
- 实时匹配高亮:在您键入模式时,确切地查看文本的哪些部分匹配
- 捕获组:查看每个组捕获的内容,无需编写调试输出
- 匹配细节:每个匹配的确切位置、长度和内容
- 替换预览:在提交之前查看查找和替换的结果
如何在线测试正则
- 输入您的模式:在模式字段中键入正则。根据需要切换标志(g表示全局,i表示不区分大小写,m表示多行)。
- 粘贴您的测试文本:输入您要匹配的文本。匹配实时高亮。
- 查看结果:查看所有匹配项及下面列出的捕获组。使用「替换为」字段测试替换。
正则表达式的简史
正则表达式由数学家Stephen Kleene于1951年在他关于神经网络的工作中作为「正则事件」的符号正式化。它们从理论跃升到实际使用,是当Ken Thompson于1968年在贝尔实验室的QED文本编辑器中实现它们时,然后在ed编辑器(1969)中,最后在grep实用程序(1973)中,其名称来自「global / regular expression / print」。
由Larry Wall于1987年引入的Perl显著扩展了正则语法:非贪婪量词、前瞻、命名组、字符类快捷方式如\d和\w。1997年发布的Perl兼容正则表达式(PCRE)库成为大多数现代语言的事实标准。
今天,几乎每种编程语言都内置了正则支持,尽管语法略有不同。JavaScript的引擎(Chrome中的V8、Firefox中的SpiderMonkey)经过高度优化,为大多数在线正则测试器提供支持。PHP、Python(re模块)和Java(java.util.regex)使用密切相关但不完全相同的语法。了解您正在为哪种风格编写对高级功能很重要。
值得了解的常见正则模式
电子邮件地址(基本):
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
URL:
https?://[^\s]+
电话号码(美国):
\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}
日期(YYYY-MM-DD):
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])
IP地址(IPv4):
\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b
十六进制颜色代码:
#(?:[0-9a-fA-F]{3}){1,2}\b
Slug(URL安全标识符):
^[a-z0-9]+(?:-[a-z0-9]+)*$
修剪空格的字符串:
^\s*(.*?)\s*$
跨语言的风格差异
正则语法主要是可移植的,但有陷阱:
- JavaScript:ES2018中添加了后顾(
(?<=...)、(?<!...)),支持Chrome 62+、Firefox 78+、Safari 16.4+。命名组通过(?<name>...)。 - Python:
re模块支持几乎所有Perl功能;第三方regex包添加了更多(可变长度后顾、模糊匹配)。 - Java:
\b表示单词边界,工作相同;命名组使用(?<name>...)。Pattern.compile + matcher模式。 - PHP:
preg_match系列在底层使用PCRE。模式需要分隔符:/模式/标志。 - Go:使用RE2语法(无反向引用、无前后顾)以保证线性性能。一些在JS/PCRE中工作的模式在Go中失败。
- Rust:
regexcrate默认使用RE2;fancy-regexcrate添加前后顾。
当您在测试器中编写正则(几乎总是JavaScript风格)时,在提交之前确认目标语言支持您使用的所有功能。
常见陷阱
- 灾难性回溯:像
(a+)+b这样的模式针对aaaaaaaaaaaaaaaaaaaaaaaac可能需要指数时间。正则引擎尝试每种可能的分组。使用占有量词((a++)+)或原子组(?>a+)+以防止这种情况。RE2和Go正则在设计上是免疫的。 - 贪婪vs惰性量词:
a.*b尽可能多地匹配(贪婪);a.*?b尽可能少地匹配(惰性)。把一个误认为另一个是「我的正则匹配太多」的#1来源。 - 多行模式中的锚点:
^和$默认匹配字符串的开始/结束。使用m标志,它们匹配每行的开始/结束。忘记这一点会给出不完整的结果。 - 字符类中的特殊字符:在
[...]内,大多数特殊字符失去其含义。[.]匹配字面点。但是]、\和^(在开头)仍然需要转义。 - Unicode和
\w:\w在大多数风格中是[A-Za-z0-9_],所以它不匹配像é这样的重音字母或非拉丁脚本。使用\p{L}(Unicode属性转义)或在JS中传递u标志。 - 忘记全局标志:没有
g,像String.matchAll()这样的方法会抛出,.replace()只替换第一个匹配。测试器默认全局,但您的实际代码可能不是。 - 锚定全输入验证:
^foo$确保整个输入是「foo」。没有锚点,foo在字符串中任何位置匹配,这在输入验证中是安全风险。
何时不使用正则
正则对某些工作来说是错误的工具:
- 解析HTML或XML:嵌套结构、属性和任意空白使正则不实用。使用适当的解析器(DOMParser、BeautifulSoup、lxml)。
- 解析JSON:同样的原因。使用
JSON.parse()或等效物。 - 解析源代码:AST解析器(Acorn、Esprima、AST-grep)理解语法。正则只看到文本。
- 电子邮件验证:RFC 5322有效电子邮件正则超过6,000个字符。对于真正的验证,改为发送确认电子邮件。
- 密码强度规则:用于「必须包含大写、小写、数字、特殊字符」的复杂正则模式难以维护。改为组合多个简单检查。
如果您发现自己正在编写超过100个字符的正则,具有多个嵌套组,那么您可能正在解决错误的问题。
编写更好正则的技巧
- 从简单开始:先让基本模式工作,然后添加复杂性。一次性尝试编写完美的正则很少奏效。
- 使用全局标志(g):没有它,测试器在第一个匹配处停止。使用
g,您看到文本中的所有匹配。 - 测试边界情况:您的正则可能匹配明显的情况但在空字符串、特殊字符或边界条件下失败。将这些添加到您的测试文本中。
- 转义特殊字符:像
.、*、+、?、(、)、[、]、{、}、\、^、$和|这样的字符在正则中有特殊含义。要字面匹配它们,前缀加反斜杠。 - 使用非捕获组:如果您需要括号进行分组但不需要捕获,使用
(?:...)而不是(...)。这使您的匹配结果更干净。 - 注释复杂模式:大多数风格支持
x标志(扩展模式),允许在模式内有空格和# 注释。对于超过50个字符的正则使用它。 - 保存您的测试:今天在您的测试文本上工作的正则应该永远在类似的输入上工作。将测试用例与您代码库中的正则一起保存作为文档。
隐私和机密测试数据
正则测试器完全在您的浏览器中使用JavaScript的原生RegExp引擎运行。您编写的模式、您粘贴的测试文本和您看到的匹配都保留在您的设备上。没有任何内容被上传、记录或由任何服务器分析。
这很重要,因为正则测试文本经常包含敏感信息:生产日志样本(带有真实用户ID、IP地址、会话令牌)、从CRM中提取的电子邮件列表、以不寻常方式格式化的客户数据。云正则测试器将所有这些通过其服务器路由,有时为「改进」目的保存它。基于浏览器的测试器对任何这些都没有暴露。
常见问题
我的正则能在其他编程语言中工作吗?
大部分正则语法在 JavaScript、Python、Java、PHP 和其他语言之间是共享的。基础模式(字符类、量词、锚点)到处有效。但高级功能如后顾断言或命名组因语言而异。
我的测试数据会发送到服务器吗?
不会。所有匹配在您的浏览器中使用 JavaScript 的原生 RegExp 引擎本地完成。没有任何内容发送到其他地方。
可以测试替换吗?
可以。输入替换模式(使用 $1、$2 等表示捕获组)以实时查看查找替换的结果。
可以离线使用吗?
可以。页面加载后,工具完全在您的浏览器中工作,无需联网。