正则表达式量词

*、+、?、{n,m} 等指定重复次数的元字符,控制前一个元素的出现次数。

正则表达式量词 (Quantifier) 是指定前一个模式元素重复次数的元字符。基本量词包括 * (0 次或多次)、+ (1 次或多次)、? (0 次或 1 次)、{n} (恰好 n 次)、{n,} (n 次或更多) 和 {n,m} (n 次以上 m 次以下)。

量词有两种行为:默认的"贪婪 (greedy)"模式和添加 ? 后的"惰性 (lazy)"模式。贪婪模式尽可能多地匹配字符,惰性模式只匹配最少的字符。例如对于字符串"<b>粗体</b>和<b>强调</b>",<b>.*</b> (贪婪) 会匹配整体,而 <b>.*?</b> (惰性) 只匹配"<b>粗体</b>"。

作为实务中的应用例,/\d{3}-\d{4}/ 匹配邮政编码 (123-4567) 的模式,/[a-zA-Z]{2,}/ 匹配 2 个字符以上的英文单词。在输入表单的校验中,可以用 {n,m} 限制字符数的范围。例如要把用户名限制为 3 到 20 个字符,可以使用 /^[a-zA-Z0-9_]{3,20}$/ 这样的模式。

使用量词时的一个重要注意点是回溯爆炸 (ReDoS: Regular Expression Denial of Service)。像 (a+)+ 这样嵌套的量词,在匹配失败时有可能耗费指数级的时间。对用户输入应用正则表达式时,建议预先验证模式的安全性,或者设置超时。

作为在语法层面压制回溯的工具,有独占量词 (possessive quantifier) 的 *+、++ 和原子组 (?>...)。两者都不会在事后放弃一旦匹配到的范围,因此不会发生失败时的组合爆炸。不过能使用独占量词的是 Perl、PCRE、Java 等,.NET 只支持原子组。截至 2026 年,JavaScript 两者都尚未纳入。在 Node.js 26 上写 a*+ 或 (?>a) 都会变成语法错误 (TC39 有提案,但尚未标准化)。在 JavaScript 中只能靠不写嵌套重复的设计来应对。

在与字符计数的关联上,量词可以直接用于字符数限制的校验。用 {n,m} 指定输入字符数的范围,可用于表单校验和数据库列长度检查。此外,用 \s+ 检测连续的空白并做规范化等文本预处理,也少不了量词。

不过,用于字符数限制时有一个陷阱:{n,m} 计数的单位与人看到的 1 个字符并不一致。不加 u 标志的 . 对应 UTF-16 的 1 个码元,因此表示家庭的表情符号 (用 ZWJ 连接 4 个人物) 会被算作 11 个,用 /^.{1,10}$/ 会被拒绝。加上 u 标志后变成以码位为单位,同一个表情符号被算作 7 个,于是能够通过。如果想与人所数的 1 个字符对齐,就不能用量词,而需要用 Intl.Segmenter 分割成书写素簇后再看个数。在没有确定以哪个单位计数的情况下就用 {n,m} 写上限,结果只有包含表情符号或结合字符的输入会得到原因不明的错误。

分享这篇文章