正则学习
正则表达式,即 Regular Expression,缩写为 Regex 或 Regexp。
我们可以使用正则表达式轻松管理数据,执行查找、匹配或编辑等命令。
介绍
Regex 是正则表达式(Regular Expression)的简称。它便于匹配、查找和管理文本。
const str = 'Understand? OK or NOT'
console.log(str.match(/OK/)) // [ 'OK', index: 12, input: 'Understand? OK or NOT', groups: undefined ]
console.log(str.match(/ok/i)) // [ 'OK', index: 12, input: 'Understand? OK or NOT', groups: undefined ]const str = 'Understand? OK or NOT'
console.log(str.match(/OK/)) // [ 'OK', index: 12, input: 'Understand? OK or NOT', groups: undefined ]
console.log(str.match(/ok/i)) // [ 'OK', index: 12, input: 'Understand? OK or NOT', groups: undefined ]正则表达式是表示搜索模式的字符串,常缩写为 Regex 或 Regexp。
它常用于查找和替换文本中的字词。此外,我们可以测试文本是否符合我们设置的规则。
例如,当我们想查找文件名列表中,扩展名是 pdf 的文件,那您只需要跟着输入 ^\w+\.pdf 就可以找到。
const strArr = ['readme.md', 'document.pdf', 'image.png', 'music.mp4', 'manual.pdf']
strArr.forEach(str => {
console.log(str.match(/^\w+\.pdf$/))
})
// null
// [ 'document.pdf', index: 0, input: 'document.pdf', groups: undefined ]
// null
// null
// [ 'manual.pdf', index: 0, input: 'manual.pdf', groups: undefined ]const strArr = ['readme.md', 'document.pdf', 'image.png', 'music.mp4', 'manual.pdf']
strArr.forEach(str => {
console.log(str.match(/^\w+\.pdf$/))
})
// null
// [ 'document.pdf', index: 0, input: 'document.pdf', groups: undefined ]
// null
// null
// [ 'manual.pdf', index: 0, input: 'manual.pdf', groups: undefined ]基本匹配
当我们想要查找字符或单词可以直接输入,就像搜索一样。例如要找出文本内的 curious 一词,只需要输入同样的内容。
const str = `
“I have no special talents. I am only passionately curious.”
― Albert Einstein
`
console.log(str.match(/curious/gm)) // [ 'curious' ]const str = `
“I have no special talents. I am only passionately curious.”
― Albert Einstein
`
console.log(str.match(/curious/gm)) // [ 'curious' ]任何字符
使用 . 可以匹配任何字符。
const str = 'abcABC123 .:!?'
console.log(str.match(/./g)) // ['a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', ' ', '.', ':', '!', '?']const str = 'abcABC123 .:!?'
console.log(str.match(/./g)) // ['a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', ' ', '.', ':', '!', '?']字符集 [abc]
如果一个词中的字符可以是各种字符,我们就将所有的可选字符写进括号 [] 中。
例如,为了查找文本中的所有单词,我们需要编写表达式,在 [] 中相邻地输入字符 a、e、i、o、u。
const str = 'bar ber bir bor bur'
console.log(str.match(/b[aeiou]r/)) // [ 'bar', index: 0, input: 'bar ber bir bor bur', groups: undefined ]
console.log(str.match(/b[aeiou]r/g)) // [ 'bar', 'ber', 'bir', 'bor', 'bur' ]const str = 'bar ber bir bor bur'
console.log(str.match(/b[aeiou]r/)) // [ 'bar', index: 0, input: 'bar ber bir bor bur', groups: undefined ]
console.log(str.match(/b[aeiou]r/g)) // [ 'bar', 'ber', 'bir', 'bor', 'bur' ]否定字符集 [^abc]
为了查找下方文本的所有单词(ber 和 bor 除外),请在 [] 中的 ^ 后面并排输入 e 和 o。
const str = 'bar ber bir bor bur'
console.log(str.match(/b[^eo]r/g)) // 'bar', 'bir', 'bur' ]const str = 'bar ber bir bor bur'
console.log(str.match(/b[^eo]r/g)) // 'bar', 'bir', 'bur' ]^ 在字符集中代表否定的意思,如果放在行首,代表以指定字符开头。
const str = 'bar ber bir bor bur'
console.log(str.match(/b[^eo]r/g)) // 'bar', 'bir', 'bur' ]
console.log(str.match(/^bar/)) // [ 'bar', index: 0, input: 'bar ber bir bor bur', groups: undefined ]const str = 'bar ber bir bor bur'
console.log(str.match(/b[^eo]r/g)) // 'bar', 'bir', 'bur' ]
console.log(str.match(/^bar/)) // [ 'bar', index: 0, input: 'bar ber bir bor bur', groups: undefined ]字母范围 [a-z]
为了查找指定范围内的字母,我们可以将起始字母和结束字母写进 [] 中,中间使用连字符 - 分割(它区分大小写)。
const str = 'abcdefghijklmnopqrstuvwxyz'
console.log(str.match(/[e-o]/g)) // ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o']const str = 'abcdefghijklmnopqrstuvwxyz'
console.log(str.match(/[e-o]/g)) // ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o']数字范围 [0-9]
为了查找指定范围的数字,我们可以在 [] 中输入起始和结束数字,中间用连字符 - 分割。
const str = '0123456789'
console.log(str.match(/[3-6]/g)) // [ '3', '4', '5', '6' ]const str = '0123456789'
console.log(str.match(/[3-6]/g)) // [ '3', '4', '5', '6' ]重复
一些特殊字符用来指定一个字符在文本中重复的次数。它们分别是加号 +、星号 * 和问号 ?。
星号 *
当我们在字符后面加上 * ,表示一个字符完全不匹配或可以匹配多次。
例如,表示字符 e 在下方文本中不出现,只出现 1 次或者并排出现多次。
const str = 'br ber beer'
console.log(str.match(/be*r/g)) // [ 'br', 'ber', 'beer' ]const str = 'br ber beer'
console.log(str.match(/be*r/g)) // [ 'br', 'ber', 'beer' ]与 + 相比不同的是可以代表字符完全不匹配。
加号 +
为了表示一个字符可以出现一次或多次,我们可以将 + 放在它后面。
例如,表示 e 在下方文本中出现一次或多次。
const str = 'br ber beer'
console.log(str.match(/be+r/g)) // [ 'ber', 'beer' ]const str = 'br ber beer'
console.log(str.match(/be+r/g)) // [ 'ber', 'beer' ]问号 ?
为了表示一个字符是可选的,我们在它后面加一个 ?。
例如,表示下方文本中的字符 u 是可选的。
const str = 'color, colour'
console.log(str.match(/colou?r/g)) // [ 'color', 'colour' ]
console.log(str.match(/colou*r/g)) // [ 'color', 'colour' ]const str = 'color, colour'
console.log(str.match(/colou?r/g)) // [ 'color', 'colour' ]
console.log(str.match(/colou*r/g)) // [ 'color', 'colour' ]我们可以使用 * 实现类似的效果,但是 * 还可以匹配多次。
const str = 'color, colouuur'
console.log(str.match(/colou?r/g)) // [ 'color' ]
console.log(str.match(/colou*r/g)) // [ 'color', 'colour' ]const str = 'color, colouuur'
console.log(str.match(/colou?r/g)) // [ 'color' ]
console.log(str.match(/colou*r/g)) // [ 'color', 'colour' ]大括号
为了表示一个字符出现的确切次数,我们可以在该字符的末尾,将它出现的次数写进大括号 {} 中,如 {n}。
例如,表示下方文本中的字符 e 只能出现 2 次。
const str = 'ber beer beeer beeeer'
console.log(str.match(/be{2}r/g)) // [ 'beer' ]
console.log(str.match(/be{3}r/g)) // [ 'beeer' ]const str = 'ber beer beeer beeeer'
console.log(str.match(/be{2}r/g)) // [ 'beer' ]
console.log(str.match(/be{3}r/g)) // [ 'beeer' ]为了表示一个字符至少出现多少次,我们可以在该字符的末尾,将它至少应出现的次数写进大括号 {} 中,并在数字后面加上逗号 ,。
例如,表示下方文本中的字母 e 至少出现 3 次。
const str = 'ber beer beeer beeeer'
console.log(str.match(/be{3,}r/g)) // [ 'beeer', 'beeeer' ]const str = 'ber beer beeer beeeer'
console.log(str.match(/be{3,}r/g)) // [ 'beeer', 'beeeer' ]为了表示一些字符出现次数在某个范围内,我们在该字符的末尾,将它至少和至多出现的次数写进大括号 {} 中,中间用逗号 , 分割,如 {x,y} 。
例如,匹配下方文本中,字符e 出现 1 到 3 次的单词。
const str = 'ber beer beeer beeeer'
console.log(str.match(/be{1,3}r/g)) // [ 'ber', 'beer', 'beeer' ]const str = 'ber beer beeer beeeer'
console.log(str.match(/be{1,3}r/g)) // [ 'ber', 'beer', 'beeer' ]组和引用
分组 ()
我们可以对一个表达式进行分组,并用这些分组来引用或执行一些规则。为了给表达式分组,我们需要将文本包裹在 () 中。
const str = 'ha-ha,haa-haa'
console.log(str.match(/(haa)/g)) // [ 'haa', 'haa' ]const str = 'ha-ha,haa-haa'
console.log(str.match(/(haa)/g)) // [ 'haa', 'haa' ]引用组(\1)
单词 ha 和 haa 分组如下。第一组用 \1 来避免重复书写。这里的 1 表示分组的顺序。
const str1 = 'ha-ha,haa-haa'
const str2 = 'ha-ha-ha,haa-haa'
console.log(str1.match(/(ha)-\1,(haa)-\2/g)) // [ 'ha-ha,haa-haa' ]
console.log(str2.match(/(ha)-\1-\1,(haa)-\2/g)) // [ 'ha-ha-ha,haa-haa' ]const str1 = 'ha-ha,haa-haa'
const str2 = 'ha-ha-ha,haa-haa'
console.log(str1.match(/(ha)-\1,(haa)-\2/g)) // [ 'ha-ha,haa-haa' ]
console.log(str2.match(/(ha)-\1-\1,(haa)-\2/g)) // [ 'ha-ha-ha,haa-haa' ]非捕获分组(?:)
我们可以对表达式进行分组,并确保它不被引用捕获。
例如,下面有两个分组,我们用 \1 引用的第一个组实际上是指向第二个组,因为第一个是未被捕获的分组。
const str = 'ha-ha,haa-haa'
console.log(str.match(/(?:ha)-ha,(haa)-\1/g)) // [ 'ha-ha,haa-haa' ]const str = 'ha-ha,haa-haa'
console.log(str.match(/(?:ha)-ha,(haa)-\1/g)) // [ 'ha-ha,haa-haa' ]竖线 |
竖线允许一个表达式包含多个不同的分支,所有分支用 | 分割。与字符层面上运作的字符集 [abc] 不同,分支在表达式层面上运作。
例如,下面的表达式同时匹配 cat 和 Cat。
const str = 'cat Cat rat'
console.log(str.match(/(c|C)at/g)) // [ 'cat', 'Cat' ]
console.log(str.match(/(c|C)at|rat/g)) // [ 'cat', 'Cat', 'rat' ]
console.log(str.match(/[cCr]at/g)) // [ 'cat', 'Cat', 'rat' ]const str = 'cat Cat rat'
console.log(str.match(/(c|C)at/g)) // [ 'cat', 'Cat' ]
console.log(str.match(/(c|C)at|rat/g)) // [ 'cat', 'Cat', 'rat' ]
console.log(str.match(/[cCr]at/g)) // [ 'cat', 'Cat', 'rat' ]转义字符 \
在书写正则表达式时,我们会用到 {}[]/\+*.$^|? 这些特殊字符。为了匹配这些特殊字符本身,我们需要通过 \ 将它们转义。
例如,要匹配文本中的 . 和 *,我们需要在它们前面添加一个 \。
const str = '(*) Asterisk.'
console.log(str.match(/(\*|\.)/g)) // [ '*', '.' ]const str = '(*) Asterisk.'
console.log(str.match(/(\*|\.)/g)) // [ '*', '.' ]插入符 ^
用来匹配字符串的开始。当我们用 [0-9] 查找数字,如果仅查找行首的数字,可以在表达式前面加上 ^。
const str = `
Basic Omellette Recipe
1. 3 eggs, beaten
2. 1 tsp sunflower oil
3. 1 tsp butter
`
console.log(str.match(/^[0-9]/gm)) // [ '1', '2', '3' ]const str = `
Basic Omellette Recipe
1. 3 eggs, beaten
2. 1 tsp sunflower oil
3. 1 tsp butter
`
console.log(str.match(/^[0-9]/gm)) // [ '1', '2', '3' ]美元符号 $
用来匹配字符串的结束。我们可以在 html 的后面添加 $,来查找仅在行末出现的 html。
const str = `
https://domain.com/what-is-html.html
https://otherdomain.com/html-elements
https://website.com/html5-features.html
`
console.log(str.match(/html$/gm)) // [ 'html', 'html' ]const str = `
https://domain.com/what-is-html.html
https://otherdomain.com/html-elements
https://website.com/html5-features.html
`
console.log(str.match(/html$/gm)) // [ 'html', 'html' ]单词字符 \w
表达式 \w 用来查找字母、数字和下划线。
const str = 'abcABC123 _.:!?'
console.log(str.match(/\w/g)) // ['a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_']const str = 'abcABC123 _.:!?'
console.log(str.match(/\w/g)) // ['a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_']非单词字符 \W
const str = 'abcABC123 _.:!?'
console.log(str.match(/\W/g)) // [ ' ', '.', ':', '!', '?' ]const str = 'abcABC123 _.:!?'
console.log(str.match(/\W/g)) // [ ' ', '.', ':', '!', '?' ]数字字符 \d
\d 仅用来匹配数字。
const str = 'abcABC123 .:!?'
console.log(str.match(/\d/g)) // [ '1', '2', '3' ]const str = 'abcABC123 .:!?'
console.log(str.match(/\d/g)) // [ '1', '2', '3' ]非数字字符 \D
\D 匹配除数字之外的字符。
const str = 'abcABC123 .:!?'
console.log(str.match(/\D/g)) // ['a', 'b', 'c', 'A', 'B', 'C', ' ', '.', ':', '!', '?']const str = 'abcABC123 .:!?'
console.log(str.match(/\D/g)) // ['a', 'b', 'c', 'A', 'B', 'C', ' ', '.', ':', '!', '?']空白符 \s
\s 仅匹配空白字符。
const str = 'abcABC123 .:!?'
console.log(str.match(/\s/g)) // [ ' ' ]const str = 'abcABC123 .:!?'
console.log(str.match(/\s/g)) // [ ' ' ]非空白字符 \S
\S 匹配除空白符之外的字符。
const str = 'abcABC123 .:!?'
console.log(str.match(/\S/g)) // ['a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '.', ':', '!', '?']const str = 'abcABC123 .:!?'
console.log(str.match(/\S/g)) // ['a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '.', ':', '!', '?']零宽断言
如果我们希望正在写的词语出现在另一个词语之前或之后,我们需要使用零宽断言。
正向先行断言 (?=)
例如,我们要匹配文本中的小时值。 为了只匹配后面有 PM 的数值,我们需要在表达式后面使用正向先行断言 (?=),并在括号内的 = 后面添加 PM。
const str = 'Date: 4 Aug 3PM'
console.log(str.match(/\d+/g)) // [ '4', '3' ]
console.log(str.match(/\d+(?=PM)/g)) // [ '3' ]const str = 'Date: 4 Aug 3PM'
console.log(str.match(/\d+/g)) // [ '4', '3' ]
console.log(str.match(/\d+(?=PM)/g)) // [ '3' ]负向先行断言 (?!)
例如,我们要在文本中匹配除小时值以外的数字。 我们需要在表达式后面使用负向先行断言 (?!),并在括号内的 ! 后面添加 PM,从而匹配没有 PM 的数值。
const str = 'Date: 4 Aug 3PM'
console.log(str.match(/\d+/g)) // [ '4', '3' ]
console.log(str.match(/\d+(?!PM)/g)) // [ '4' ]const str = 'Date: 4 Aug 3PM'
console.log(str.match(/\d+/g)) // [ '4', '3' ]
console.log(str.match(/\d+(?!PM)/g)) // [ '4' ]正向后行断言 (?<=)
例如,我们要匹配文本中的金额数。 为了匹配前面带有 $ 的数字。我们需要在表达式前面使用正向后行断言 (?<=) ,并在括号内的 = 后面添加 \$。
const str = 'Product Code: 1064 Price: $5'
console.log(str.match(/(?<=\$)\d+/g)) // [ '5' ]const str = 'Product Code: 1064 Price: $5'
console.log(str.match(/(?<=\$)\d+/g)) // [ '5' ]负向后行断言 (?<!)
例如,我们要在文本中匹配除价格外的数字。 为了只匹配前面没有 $ 的数字,我们要在表达式前用负向后行断言 (?<!),并在括号内的 ! 后面添加 \$。
const str = 'Product Code: 1064 Price: $5'
console.log(str.match(/(?<!\$)\d+/g)) // [ '1064' ]const str = 'Product Code: 1064 Price: $5'
console.log(str.match(/(?<!\$)\d+/g)) // [ '1064' ]标志
标志可以改变表达式的输出,这也是标志被称为修饰符的原因。
标志决定表达式是否将文本视作单独的行处理,是否区分大小写,或者是否查找所有匹配项。
全局标志
全局标志使表达式选中所有匹配项,如果不启用全局标志,表达式只会匹配第一个匹配项。
const str = 'domain.com, test.com, site.com'
console.log(str.match(/\w+\.com/)) // ['domain.com']
console.log(str.match(/\w+\.com/g)) // [ 'domain.com', 'test.com', 'site.com' ]const str = 'domain.com, test.com, site.com'
console.log(str.match(/\w+\.com/)) // ['domain.com']
console.log(str.match(/\w+\.com/g)) // [ 'domain.com', 'test.com', 'site.com' ]多行标志
正则表达式将所有文本视作一行。如果我们使用了多行标志,它就会单独处理每一行。
const str34 = `
domain.com
test.com
site.com
`
console.log(str34.match(/\w+\.com$/)) // null
console.log(str34.match(/\w+\.com$/gm)) // [ 'domain.com', 'test.com', 'site.com' ]const str34 = `
domain.com
test.com
site.com
`
console.log(str34.match(/\w+\.com$/)) // null
console.log(str34.match(/\w+\.com$/gm)) // [ 'domain.com', 'test.com', 'site.com' ]忽略大小写标志
忽略大小写标志可以让我们编写的表达式不再大小写敏感。
const str35 = `
DOMAIN.COM
TEST.COM
SITE.COM
`
console.log(str35.match(/\w+\.com$/gm)) // null
console.log(str35.match(/\w+\.com$/gim)) // [ 'DOMAIN.COM', 'TEST.COM', 'SITE.COM' ]const str35 = `
DOMAIN.COM
TEST.COM
SITE.COM
`
console.log(str35.match(/\w+\.com$/gm)) // null
console.log(str35.match(/\w+\.com$/gim)) // [ 'DOMAIN.COM', 'TEST.COM', 'SITE.COM' ]贪婪匹配
正则表达式默认执行贪婪匹配。这意味着匹配的内容会尽可能长。
const str = 'ber beer beeer beeeer'
console.log(str.match(/.*r/)) // ['ber beer beeer beeeer']const str = 'ber beer beeer beeeer'
console.log(str.match(/.*r/)) // ['ber beer beeer beeeer']惰性匹配
与贪婪匹配不同,惰性匹配会在第一次匹配时停止。
下面的例子中,在 * 之后添加 ?,将查找以 r 结尾且前面嗲有任意字符的第一个匹配项。
const str = 'ber beer beeer beeeer'
console.log(str.match(/.*?r/)) // ['ber']const str = 'ber beer beeer beeeer'
console.log(str.match(/.*?r/)) // ['ber']备忘单
