正则表达式测试
用自己的文本测试 JavaScript 正则表达式。可切换 g、i、m、s、u 标志,查看每个匹配的位置与捕获组,并实时预览替换结果。
边输入边看到每一个匹配、每一个分组和每一个位置。 输入模式和待匹配的文本。每条结果都会列出位置和捕获组;模式非法时会显示引擎自身的报错信息;替换框则精确展示一次 replace 调用会产生什么。
用自己的文本测试 JavaScript 正则表达式。可切换 g、i、m、s、u 标志,查看每个匹配的位置与捕获组,并实时预览替换结果。
边输入边看到每一个匹配、每一个分组和每一个位置。 输入模式和待匹配的文本。每条结果都会列出位置和捕获组;模式非法时会显示引擎自身的报错信息;替换框则精确展示一次 replace 调用会产生什么。
这五个标志不是偏好设置,它们会重新定义整个模式。没有 g 时,搜索找到第一个匹配就停止;加上它,引擎会维护一个 lastIndex 游标遍历整个字符串——这也是同一个全局正则在多次调用间复用时,看起来会「跳过」匹配的原因。i 标志按 Unicode 大小写映射折叠大小写,而不是简单的 ASCII 位移。
最容易被误解的是 m 和 s。m 只改变 ^ 和 $ 的含义,把它们锚定到行边界而非整个字符串,对 . 毫无影响;反过来 s 只改变 .,让它能匹配换行符。记住 "dotall" 这个名字最有用。u 标志让模式按码点而非 UTF-16 码元工作,于是 . 能把一个 emoji 当作一个整体匹配,\u{1F600} 这样的转义也变得合法。它同时会把过去被容忍的转义变成错误——这就是给已有模式加上它会突然报错的原因。
每一对圆括号默认都会捕获,按左括号出现的顺序编号,替换时的 $1、$2 引用的正是这些编号。嵌套遵循同样的规则,所以在 ((a)b) 中第一组是 ab,第二组是 a。当你只是为了分支或重复而需要分组时,用 (?:...) 可以不分配捕获,也让编号更易读。
每个匹配所报告的位置,是首个被匹配字符的偏移量,其意义比听上去更大。它让你能区分位于不同位置的两个相同匹配,确认锚点是否如预期生效,并诊断经典的零长度匹配。像 \d* 这样的模式在任何地方都能匹配空串,于是全局扫描会在每个位置都找到一个匹配;引擎之所以还能推进,是因为实现会在匹配为空时强制把游标前移。
包括 JavaScript 在内的多数引擎使用回溯。当模式的某一部分失败时,引擎会退回上一个决策点尝试另一条路径。通常这不可见。一旦嵌套量词让路径数量指数级增长,它就会变成灾难,经典形态是用 (a+)+b 去匹配一长串没有 b 的 a。这种输入能让浏览器标签页卡死;如果模式来自用户输入,它就成了拒绝服务的攻击面。
两个习惯几乎能规避全部问题:优先使用具体的字符类而非 .*,以免多个分支互相重叠;能加锚点就加锚点,因为带锚点的失败会被立即否定,而不是在每个偏移位置重试一遍。本工具还把结果上限设为 500 条并在截断时给出提示,这样失控的模式只会退化为较慢的结果,而不是冻结页面。