MiServe DevTools

我的贴纸

把工具固定在这里,会保存在此浏览器中。

正则表达式测试(JavaScript 与 Go RE2)

用 JavaScript 引擎或 Go 的 RE2 引擎测试正则表达式,查看每一处匹配、每个捕获组以及替换结果。

标志

文本中的匹配

两种引擎的语法速查表
写法含义JavaScriptGo (RE2)
. 除换行符外的任意字符。使用 s 标志时也匹配换行符。 支持 支持
\d \w \s 一个数字、一个单词字符、一个空白字符。大写的 \D、\W 和 \S 匹配相反的内容。 支持 支持
[abc] [^abc] [a-z] 集合中的一个字符、不在集合中的一个字符、某个范围内的一个字符。 支持 支持
^ $ 文本的开头和结尾。使用 m 标志时,匹配每一行的开头和结尾。 支持 支持
\b \B 单词边界,以及不是单词边界的位置。 支持 支持
* + ? {2,5} 零次或多次、一次或多次、零次或一次、2 到 5 次。每个量词都尽可能多地匹配。 支持 支持
*? +? ?? {2,5}? 同样的量词,但尽可能少地匹配。 支持 支持
(…) (?:…) 捕获组,以及只分组、不捕获的组。 支持 支持
(?<name>…) 命名捕获组。Go 从 1.22 版起支持这种写法,所有版本都支持 (?P<name>…)。 支持 支持
a|b a 或 b。 支持 支持
\p{L} \p{Lu} Unicode 属性:任意字母、任意大写字母。JavaScript 需要 u 标志才能使用。 支持 支持
(?i) (?s) (?m) 写在模式内部的标志。JavaScript 的标志设在模式外部。 不支持 支持
\A \z 整个文本的开头和结尾,不受 m 标志影响。 不支持 支持
[[:alpha:]] 集合内的 POSIX 字符类。 不支持 支持
(?=…) (?!…) 先行断言:后面的内容必须匹配或必须不匹配,且不消耗字符。 支持 不支持
(?<=…) (?<!…) 后行断言:前面的内容必须匹配或必须不匹配。 支持 不支持
\1 \k<name> 反向引用:与前面某个分组所匹配的文本相同。 支持 不支持

通过 WebAssembly 在浏览器中运行。粘贴的内容不会被上传。

关于此工具

两类引擎

正则表达式引擎分为两类。回溯引擎,如 PCRE 以及 JavaScript、Python、Java 和 .NET 的默认引擎,会沿着模式尝试一条路径,失败时再退回。这种设计让先行断言、后行断言和反向引用成为可能。自动机引擎,如 RE2 和 Go 的 regexp 包,会同时跟踪所有可能的路径,因此运行时间随文本长度线性增长。代价是不支持环视和反向引用。

这里的 JavaScript 引擎就是你浏览器自带的 RegExp。Go 引擎是编译为 WebAssembly 的 Go 标准库 regexp 包,所以在这里执行你的模式的代码,和在 Go 程序中执行它的代码是同一份。Go 接受 RE2 语法。

解读结果

每处匹配都会在文本中加框并高亮,用两种颜色交替显示,以便区分紧挨在一起的匹配。空匹配,例如 x* 在两个字母之间找到的匹配,显示为一条细竖线。表格列出每处匹配的起止位置和每个捕获组的文本。没有参与匹配的分组会标记为未匹配,这与匹配到空字符串的分组不同。

位置按 UTF-16 码元从 0 开始计数,不包含结束位置:也就是在 JavaScript 中传给 String.prototype.slice 的数字。Go 本身报告的是 UTF-8 文本中的字节偏移量;此工具会进行换算,因此两种引擎显示的数字相同。

在 JavaScript 中,不带 g 标志的模式只查找第一处匹配,也只替换第一处匹配。Go 引擎总是报告所有匹配,与 FindAllStringSubmatchIndex 和 ReplaceAllString 的行为一致。

灾难性回溯

当量词嵌套或重叠时,回溯引擎可能需要指数级的时间。经典的例子是用 (a+)+$ 去匹配一长串 a 后跟另一个字符:引擎在放弃之前,会尝试在内外两个量词之间拆分这串字符的每一种方式。在服务器上,这是一种称为 ReDoS 的拒绝服务风险。

此工具在后台 Worker 中运行 JavaScript 模式,并在 1.5 秒后终止该 Worker,因此页面能保持响应,并告诉你发生了什么。用 Go 引擎测试同一个模式,可以快速判断变慢是否源于回溯,因为 Go 保证运行时间与文本长度呈线性关系。

常见问题

为什么我的模式在 JavaScript 中可用,在 Go 中却失败?

常见原因是先行断言、后行断言或反向引用,RE2 为了保证线性运行时间而舍弃了这些功能。标志是另一个常见原因:Go 没有写在模式外部的标志,所以不区分大小写的匹配要在模式开头写 (?i)。

JavaScript 和 Go 的替换字符串有什么不同?

两者都用 $1 表示第一个分组。命名分组在 JavaScript 中写作 $<name>,在 Go 中写作 ${name}。在 Go 中,$1x 表示名为 1x 的分组,所以当引用后面紧跟字母、数字或下划线时,要写成 ${1}x。

可以在这里测试 PCRE、Python 或 Java 的模式吗?

此页面只运行 JavaScript 和 Go 两种引擎。各种正则方言的常用语法是相通的,所以对于简单的模式,JavaScript 引擎是不错的近似,但占有量词、原子组和递归等功能,这里的两种引擎都不支持。

我测试的文本会被发送到服务器吗?

不会。两种引擎都在你的浏览器中运行:JavaScript 在后台 Worker 中运行,Go 以 WebAssembly 模块的形式运行。此工具不会用你的模式或文本发出任何网络请求,网站的安全策略也会阻止向其他任何主机发出请求。

文本分类的其他工具