Skip to content

正则表达式

是什么

从字符串中查找符合某个规则的子字符串的时候,往往会用到正则表达式。

为什么

相较于遍历字符串编写查找规则的原始方式,正则表达式往往更加简洁,减少了很多复杂规则的编写,但是也有不少问题:性能差的表达式会被 DDoS 攻击,难以阅读等。

语法备忘清单

INFO

示例使用 js(便于在浏览器中运行快速查看结果),但正则表达式基本是通用写法,支持通用参考。不同正则表达式处理引擎可能会存在某些特性不支持,需注意。

js 中,regexp.test(string) 方法检查字符串是否满足正则表达式,string.match(regexp) 获取正则表达式匹配的结果。

○ 常规匹配

js
// 字符集“.”不匹配换行
// 数量“+”匹配数量 >= 1
// 数量“*”匹配数量 >= 0
// 数量“?”匹配数量 0 或 1,即左侧字符可选
// 数量“{n,m}”匹配数量 [n, m]
// 范围“g”表示全局匹配,否则找到第一个满足的子字符串就会结束
// 范围“m”表示多行匹配,每一行单独匹配,否则只匹配所有行
// 范围“i”表示忽略大小写,否则严格大小写匹配
"abc\ndef".match(/.+/gmi) // abc, def

// 字符集“[a-zA-Z0-9_]”表示匹配 a 到 z 的大小写,0 到 9,下划线
// 字符集“\w”(小写)表示匹配 [a-zA-Z0-9_]
// 字符集“\W”(大写)表示匹配 [^\w],即 \w 范围取反
// 字符集“\d”表示匹配 [0-9]
// 字符集“\s”表示匹配空白字符,[\t\n\f\r]
// ⚠️\p 相关字符集不一定生效,与正则表达式引擎支持相关
// 字符集“\p{L}”表示匹配任意字母(包含拉丁字母、汉字、阿拉伯字母)
// 字符集“\p{N}”表示匹配任意数字
// 字符集“\p{Z}”表示匹配任意空白字符
"中文_abc".match(/\w+/) // _abc
"123_abc".match(/\d+/) // 123
"\t\n \f\r".match(/\s/g) // \t, \n, 空格,\f, \r
"中文_abcL_123\t\n \f\r".match(/\p{L}+/)
"中文_abcN_123\t\n \f\r".match(/\p{N}+/)
"中文_abcZ_123\t\n \f\r".match(/\p{Z}+/)

// 位置“^”表示以后一个字符开头,即 B
// 位置“$”表示以前一个字符结尾,即 t
// 位置“\b”表示边界前或后的单词
"ag\nbg\ncg".match(/\wg$/g) // cg
"ag\nbg\ncg".match(/\wg$/gm) // ag, bg, cg
"Boat".match(/^B[^t]+t$/) // Boat
"cat dog pig".match(/\bd\w*/) // dog

○ 分组匹配

js
// 分组“()”表示匹配分组中的有序内容,“|” 表示或
// 💡不带 g 会得到完整匹配结果 + 分组结果
"css_scss_less_postcss".match(/(css|scss)/g) // css, scss, css
"css_scss_less_postcss".match(/(css|scss)/) // css, css

// 分组中的“?”表示不捕获分组,此处捕获、不捕获结果相同(我也不理解🤔)
"The car is parked in the garage.".match(/(c|g|p)ar/g) // car, par, gar
"The car is parked in the garage.".match(/(?:c|g|p)ar/g) // car, par, gar

○ 前后临近匹配(零宽断言)

前表示往右,后表示往左

js
// 分组“(?=)”匹配前向为某字符时的后向字符,该分组中的内容不返回(不捕获)
// 分组“(?!)”匹配前向不为某字符时的后向字符
"The fat cat sat on the mat.".match(/(the(?=\sfat))/gi) // The
"The fat cat sat on the mat.".match(/(the(?!\sfat))/gi) // the

// 分组“(?<=)”匹配后向为某字符时的前向字符
// 分组“(?<!)”匹配后向不为某字符时的前向字符
"The fat cat sat on the mat.".match(/(?<=the\s)(fat|mat)/g) // mat
"The fat cat sat on the mat.".match(/(?<!the\s)(fat|mat)/g) // fat

○ 贪婪懒惰

js
// 数量贪婪“*”,尽可能长
"a_a_a_a_a".match(/(.*a)/) // a_a_a_a_a, a_a_a_a_a
"a_a_a_a_a".match(/(.*a)/g) // a_a_a_a_a
// 数量懒惰“*?”,尽可能短
"a_a_a_a_a".match(/(.*?a)/) // a, a
"a_a_a_a_a".match(/(.*?a)/g) // a, _a, _a, _a, _a

○ ReDoS 攻击

不当的正则表达式,会造成机器性能下降甚至崩溃。

避免方法:

  1. 避免大量回溯的正则表达式
  2. 限制输入长度
  3. 提前测试性能
js
// 分组内外往多匹配,大量回溯
"accccccccccc".match(/(^c+)+/)
"acccccccccccccccccccccccccccc".match(/(.*c){11}/)

参考

访问量 PV:Blocked用户数 UV:Blocked