JavaScript 正则表达式速查表和示例
这篇博文概述了 JavaScript 支持的正则表达式语法和特性。示例已在 Chrome/Chromium 控制台(版本 81 及以上)上测试通过,并包含其他浏览器和平台所不具备的功能。除非另有说明,否则默认使用 ASCII 字符集。本文节选自我的JavaScript 正则表达式书籍。
定义正则表达式的元素
| 笔记 | 描述 |
|---|---|
| MDN:正则表达式 | MDN 文档中关于 JavaScript 正则表达式的内容 |
/pat/ |
正则表达式对象 |
const pet = /dog/ |
将正则表达式保存到变量中以便重复使用、提高清晰度等等。 |
/pat/.test(s) |
检查给定的模式是否出现在输入字符串中的任何位置 |
退货true或false |
|
i |
标记用于忽略字母匹配时的大小写。 |
g |
标记以匹配所有出现情况 |
new RegExp('pat', 'i') |
从字符串构建正则表达式 |
| 第二个参数指定标志。 | |
使用反引号字符串${}进行插值 |
|
source |
用于将正则表达式对象转换为字符串的属性 |
| 有助于将一个正则表达式插入到另一个正则表达式中 | |
flags |
获取正则表达式对象标志的属性 |
s.replace(/pat/, 'repl') |
搜索和替换方法 |
s.search(/pat/) |
给出比赛的起始位置或-1 |
s.split(/pat/) |
根据正则表达式分割字符串 |
| 锚 | 描述 |
|---|---|
^ |
将匹配范围限制在字符串的开头 |
$ |
将匹配范围限制在字符串末尾。 |
m |
标记与行的开头/结尾^和$锚点匹配 |
\r, \n,\u2028和\u2029是行分隔符 |
|
使用 DOS 风格文件\r\n可能需要特别注意 |
|
\b |
将匹配范围限制在单词的开头/结尾。 |
| 单词字符:字母、数字、下划线 | |
\B |
\b凡是不匹配的地方都匹配 |
^上表中的 `\` 、`\ $` 和`\`\是元字符\,因为这些字符具有特殊含义。给字符添加前缀可以移除其特殊含义,使其按字面意思匹配。例如,` \`\^将匹配一个^字符,而不是作为锚点。
| 特征 | 描述 |
|---|---|
pat1|pat2|pat3 |
多个正则表达式组合成 OR 条件 |
| 每个备选方案都可以有独立的锚点。 | |
(pat) |
群体模式,也称捕获群体 |
a(b|c)d |
与……相同abd|acd |
(?:pat) |
非捕获组 |
(?<name>pat) |
命名捕获组 |
. |
匹配除行分隔符以外的任何字符 |
[] |
角色类别,与众多角色中的一个相匹配 |
| 贪婪量词 | 描述 |
|---|---|
? |
比赛0或1时间 |
* |
比赛0或更多次 |
+ |
比赛1或更多次 |
{m,n} |
比赛m时间n |
{m,} |
至少匹配m次 |
{n} |
精确匹配n次数 |
pat1.*pat2 |
pat1介于两者之间的任意数量的字符pat2 |
pat1.*pat2|pat2.*pat1 |
两者匹配pat1,pat2顺序不限 |
这里的“贪婪”指的是上述量词会尽可能多地匹配符合正则表达式整体规则的内容。?在贪婪量词后添加 `a` 会将其变为非贪婪量词,即尽可能少地匹配内容。量词可以应用于字面字符、字符组、反向引用和字符类。
| 角色职业 | 描述 |
|---|---|
[ae;o] |
匹配以下任意一个字符 |
[3-7] |
字符范围3从到7 |
[^=b2] |
否定集,匹配除“或”=或“b或”之外的其他项2 |
[a-z-] |
-应该是首尾呼应,或者使用转义\符进行字面匹配。 |
[+^] |
^不应是首字符或使用转义字符\ |
[\]\\] |
]应该\使用转义\ |
\w |
类似于[A-Za-z0-9_]匹配单词字符 |
\d |
[0-9]与匹配数字字符类似 |
\s |
类似于[ \t\n\r\f\v]匹配空白字符 |
使用\W、、\D和\S分别表示它们的反义词 |
|
u |
启用 Unicode 匹配的标志 |
\p{} |
Unicode字符集 |
\P{} |
否定 Unicode 字符集 |
| 详情请参阅MDN:Unicode 属性转义。 | |
\u{} |
使用代码点指定Unicode字符 |
| 环顾四周 | 描述 |
|---|---|
| 环顾四周 | 允许创建自定义的肯定/否定断言 |
| 零宽度锚点,且不属于匹配部分 | |
(?!pat) |
负面前瞻断言 |
(?<!pat) |
负面后视断言 |
(?=pat) |
积极展望断言 |
(?<=pat) |
正面回溯断言 |
| 允许可变长度的后向断言。 | |
(?!pat1)(?=pat2) |
可以按任意顺序并排指定多个断言。 |
| 它们标记匹配的位置,而无需消耗字符。 | |
((?!pat).)* |
对正则表达式模式取反 |
| 匹配部分 | 描述 |
|---|---|
m = s.match(/pat/) |
假设g未使用标志且正则表达式匹配成功, |
| 返回一个包含匹配部分和 3 个属性的数组 | |
index该属性给出比赛的起始位置。 |
|
input属性给出输入字符串s |
|
groups该属性提供命名捕获组的字典 |
|
m[0] |
对于上述情况,给出整个匹配部分 |
m[N] |
第N个捕获组的匹配部分 |
s.match(/pat/g) |
仅返回匹配的部分,不返回任何属性。 |
s.matchAll(/pat/g) |
返回一个包含详细信息的迭代器 |
| 每个匹配部分及其属性 | |
| 反向引用 | 给出第N个捕获组的匹配部分 |
在替换部分使用$1,,,$2等等$3 |
|
$&提供整个匹配部分 |
|
$`给出匹配部分之前的字符串 |
|
$'返回匹配部分之后的字符串 |
|
在正则表达式定义中使用\1逗号、\2逗号、逗号等\3 |
|
$$ |
直接插入$替换部分 |
$0N |
与此相同$N,允许分离反向引用和其他数字。 |
\N\xhh |
允许在正则表达式定义中分离反向引用和数字。 |
(?<name>pat) |
命名捕获组 |
用于\k<name>正则表达式定义中的反向引用 |
|
用于$<name>替换部分的反向引用 |
正则表达式示例
test方法
> let sentence = 'This is a sample string'
> /is/.test(sentence)
< true
> /xyz/.test(sentence)
< false
> if (/ring/.test(sentence)) {
console.log('mission success')
}
< mission success
new RegExp()构造函数
> new RegExp('dog', 'i')
< /dog/i
> new RegExp('123\\tabc')
< /123\tabc/
> let greeting = 'hi'
> new RegExp(`${greeting.toUpperCase()} there`)
< /HI there/
- 绳索和锚
// string anchors
> /^cat/.test('cater')
< true
> ['surrender', 'newer', 'door'].filter(w => /er$/.test(w))
< ["surrender", "newer"]
// use 'm' flag to change string anchors to line anchors
> /^par$/m.test('spare\npar\nera\ndare')
< true
// escape metacharacters to match them literally
> /b\^2/.test('a^2 + b^2 - C*3')
< true
replace方法和词语边界
> let items = 'catapults\nconcatenate\ncat'
> console.log(items.replace(/^/gm, '* '))
< * catapults
* concatenate
* cat
> let sample = 'par spar apparent spare part'
// replace 'par' only at the start of word
> sample.replace(/\bpar/g, 'X')
< "X spar apparent spare Xt"
// replace 'par' at the end of word but not whole word 'par'
> sample.replace(/\Bpar\b/g, 'X')
< "par sX apparent spare part"
- 交替和分组
// replace either 'cat' at start of string or 'cat' at end of word
> 'catapults concatenate cat scat'.replace(/^cat|cat\b/g, 'X')
< "Xapults concatenate X sX"
// same as: /\bpark\b|\bpart\b/g
> 'park parked part party'.replace(/\bpar(k|t)\b/g, 'X')
< "X parked X party"
- MDN:正则表达式文档提供了
escapeRegExp一个函数,可用于自动转义元字符。- 另请参阅XRegExp实用程序,它提供了XRegExp.escape和XRegExp.union方法。union 方法具有允许混合使用字符串和正则表达式字面量的附加功能,并且还可以处理反向引用的重新编号。
> function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&')
}
> function unionRegExp(arr) {
return arr.map(w => escapeRegExp(w)).join('|')
}
> new RegExp(unionRegExp(['c^t', 'dog$', 'f|x']), 'g')
< /c\^t|dog\$|f\|x/g
- 点元字符和量词
// matches character '2', any character and then character '3'
> '42\t33'.replace(/2.3/, '8')
< "483"
// 's' flag will allow line separators to be matched as well
> 'Hi there\nHave a Nice Day'.replace(/the.*ice/s, 'X')
< "Hi X Day"
// same as: /part|parrot|parent/g
> 'par part parrot parent'.replace(/par(en|ro)?t/g, 'X')
< "par X X X"
> ['abc', 'ac', 'abbc', 'xabbbcz'].filter(w => /ab{1,4}c/.test(w))
< ["abc", "abbc", "xabbbcz"]
match方法
// entire matched portion
> 'abc ac adc abbbc'.match(/a(.*)d(.*a)/)[0]
< "abc ac adc a"
// matched portion of 2nd capture group
> 'abc ac adc abbbc'.match(/a(.*)d(.*a)/)[2]
< "c a"
// get location of matching portion
> 'cat and dog'.match(/dog/).index
< 8
// get all matching portions with 'g' flag
// no properties or group portions
> 'par spar apparent spare part'.match(/\bs?par[et]\b/g)
< ["spare", "part"]
// useful for debugging purposes as well before using 'replace'
> 'that is quite a fabricated tale'.match(/t.*?a/g)
< ["tha", "t is quite a", "ted ta"]
matchAll方法
// same as: match(/ab*c/g)
> Array.from('abc ac adc abbbc'.matchAll(/ab*c/g), m => m[0])
< ["abc", "ac", "abbbc"]
// get index for each match
> Array.from('abc ac adc abbbc'.matchAll(/ab*c/g), m => m.index)
< [0, 4, 11]
// get only capture group portions as an array for each match
> let s = 'xx:yyy x: x:yy :y'
> Array.from(s.matchAll(/(x*):(y*)/g), m => m.slice(1))
< (4) [Array(2), Array(2), Array(2), Array(2)]
0: (2) ["xx", "yyy"]
1: (2) ["x", ""]
2: (2) ["x", "yy"]
3: (2) ["", "y"]
length: 4
__proto__: Array(0)
- 替换部分中的函数/字典
> function titleCase(m, g1, g2) {
return g1.toUpperCase() + g2.toLowerCase()
}
> 'aBc ac ADC aBbBC'.replace(/(a)(.*?c)/ig, titleCase)
< "Abc Ac Adc Abbbc"
> '1 42 317'.replace(/\d+/g, m => m*2)
< "2 84 634"
> let swap = { 'cat': 'tiger', 'tiger': 'cat' }
> 'cat tiger dog tiger cat'.replace(/cat|tiger/g, k => swap[k])
< "tiger cat dog cat tiger"
split方法
// split based on one or more digit characters
> 'Sample123string42with777numbers'.split(/\d+/)
< ["Sample", "string", "with", "numbers"]
// include the portion that caused the split as well
> 'Sample123string42with777numbers'.split(/(\d+)/)
< ["Sample", "123", "string", "42", "with", "777", "numbers"]
// split based on digit or whitespace characters
> '**1\f2\n3star\t7 77\r**'.split(/[\d\s]+/)
< ["**", "star", "**"]
// use non-capturing group if capturing is not needed
> '123handed42handy777handful500'.split(/hand(?:y|ful)?/)
< ["123", "ed42", "777", "500"]
- 与普通/非捕获/命名捕获组进行反向引用
// remove consecutive duplicate words separated by space
// use \W+ instead of space to cover cases like 'a;a<-;a'
> 'aa a a a 42 f_1 f_1 f_13.14'.replace(/\b(\w+)( \1)+\b/g, '$1')
< "aa a 42 f_1 f_13.14"
// add something around the entire matched portion
> '52 apples and 31 mangoes'.replace(/\d+/g, '($&)')
< "(52) apples and (31) mangoes"
// duplicate first field and add it as last field
> 'fork,42,nice,3.14'.replace(/,.+/, '$&,$`')
< "fork,42,nice,3.14,fork"
// use non-capturing groups when backreferencing isn't needed
> '1,2,3,4,5,6,7'.replace(/^((?:[^,]+,){3})([^,]+)/, '$1($2)')
< "1,2,3,(4),5,6,7"
// named capture groups, same as: replace(/(\w+),(\w+)/g, '$2,$1')
> 'good,bad 42,24'.replace(/(?<fw>\w+),(?<sw>\w+)/g, '$<sw>,$<fw>')
< "bad,good 24,42"
- 环视示例
// change 'foo' only if it is not followed by a digit character
// note that end of string satisfies the given assertion
// note that 'foofoo' has two matches
> 'hey food! foo42 foot5 foofoo'.replace(/foo(?!\d)/g, 'baz')
< "hey bazd! foo42 bazt5 bazbaz"
// change whole word only if it is not preceded by : or --
> ':cart apple --rest ;tea'.replace(/(?<!:|--)\b\w+/g, 'X')
< ":cart X --rest ;X"
// extract digits only if it is preceded by - and followed by , or ;
> '42 foo-5, baz3; x83, y-20; f12'.match(/(?<=-)\d+(?=[;,])/g)
< ["5", "20"]
// words containing all vowels in any order
> let words = ['sequoia', 'questionable', 'exhibit', 'equation']
> words.filter(w => /(?=.*a)(?=.*e)(?=.*i)(?=.*o).*u/.test(w))
< ["sequoia", "questionable", "equation"]
// replace only 3rd occurrence of 'cat'
> 'cat scatter cater scat'.replace(/(?<=(cat.*?){2})cat/, 'X')
< "cat scatter Xer scat"
// match if 'do' is not there between 'at' and 'par'
> /at((?!do).)*par/.test('fox,cat,dog,parrot')
< false
调试和可视化工具
随着正则表达式变得越来越复杂,一旦出现问题,调试起来就会非常困难。从头开始逐步构建正则表达式,并用输入字符串进行测试,对于解决问题大有裨益。为了辅助这个过程,您可以使用各种在线正则表达式工具。
regex101是一个很受欢迎的正则表达式测试网站。首先,你需要选择 JavaScript 版本。然后,你可以添加正则表达式、输入字符串、选择标志以及可选的替换字符串。匹配的部分会被高亮显示,并在单独的窗格中提供解释。此外,它还提供快速参考和其他功能,例如分享、代码生成器、测试等等。
另一个有用的工具是jex:regulex,它可以将你的正则表达式转换为铁路图,从而提供可视化的帮助来理解模式。
JavaScript 正则表达式书籍
访问我的代码仓库learn_js_regexp,了解我撰写的关于 JavaScript 正则表达式的书籍详情。这本电子书使用了大量示例,从基础概念讲解,并包含练习题来检验你的理解。本文中的速查表和示例均基于本书内容。
文章来源:https://dev.to/learnbyexample/javascript-regular-expressions-cheatsheet-and-examples-4jg7

