创建一个正则表达式
方法一:使用正则表达式字面量
1 | const reg = /正则表达式/匹配模式 |
方法二:调用 RegExp 对象的构造函数
1 | const reg = new RegExp("正则表达式","匹配模式(可选)") |
- 注:使用 typeof 检查正则对象,会返回 object
匹配模式
- 『i』:忽略大小写
- 『g』:全局匹配模式
- 『m』:多行匹配,使边界字符 ^ 和 $ 匹配每一行的开头和结尾
- 『s』:(es2018)匹配单个字符,与.一起使用,使.代表一切字符,称为 dotAll 模式
- 使用 reg.dotAll 来判断该正则表达式是否处于动 dotAll 模式
正则表达式规则
- 『^』:匹配开头
- 『$』:匹配结尾
- 『\w』:匹配任意字母、数字、_ ([A-z0-9_])
- 『\W』:匹配除了字母、数字、_ ([^A-z0-9_])
- 『\d』:匹配任意的数字 ([0-9])
- 『\D』:匹配除了数字 ([^0-9])
- 『\s』:匹配空格
- 『\S』:匹配除了空格
- 『\b』:单词边界(在单词两边使用,表示在这个字符串中完整匹配这个单词)
- 『\B』:匹配除了单词边界
- 『[a-z]』:任意小写字母
- 『[A-Z]』:任意大写字母
- 『[A-z]』:任意字母
- 『[0-9]』:任意数字
- 『[ ]』:匹配括号内的任意一个字符。
- 『[^ ]』:匹配除了括号内的字符以外的任意一个字符
量词 — 只对它前边的一个内容起作用
- 『{n}』:正好出现 n 次
- 『{m,n}』:出现 m-n 次
- 『{m,}』:m 次以上
- 『+』:至少一个 == {1,}
- 『*』:0 个或多个 == {0,}
- 『?』:0 个或 1 个 == {0,1}
- 『.』:表示任意字符(除了换行符)
- 注:『*、+ 都是贪婪的,会尽可能匹配更多的文字,在它们后面加上?就可以实现非贪婪或最小匹配』
特殊字符
- 『
\
』: 转义字符,用于匹配特殊字符本身 - 『|』:用于指定多个模式的选择
- 『( )』:用于分组和捕获子表达式
- 『(?: )』:用于分组但不捕获子表达式
- 『?=』:exp1(?=exp2):查找 exp2 前面的 exp1,即『先行断言』
- 『?<=』:(?<=exp2)exp1:查找 exp2 后面的 exp1,即『后行断言』,es2018
- 『?!』:exp1(?!exp2):查找后面不是 exp2 的 exp1,即『先行否定断言』
- 『?<!』:exp1(?<!exp2):查找前面不是 exp2 的 exp1,即『后行否定断言』,es2018
- 『\1』:指定第一个子匹配项。
1 | eg1:去除开头的空格 |
常见的匹配规则
- 匹配 HTML 标记
1
<[a-zA-Z]+.*?>([\s\S]*?)</[a-zA-Z]*?>
- 匹配空行
1
/^\s*$/
- 一个单词连续出现的位置
1
/\b([a-z]+) \1\b/gi
- 匹配一个 URL 解析为协议、域、端口及相对路径
1
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/
- 定位章节的位置
1
/^(?:Chapter|Section) [1-9][0-9]{0,1}$/
合法手机号
- 手机号的规则:
- 以 1 开头
- 第二位 3-9 任意数字
- 三位以后任意数字 9 个
- 匹配模式:
1
/^1[3-9][0-9]{9}$/
邮箱
- 邮件的规则:
- 前边可以是任意字符
- 跟着一个@
- 后边可以是任意字符
- 后面是.com | .cn 等
- 匹配模式:
1
[\w.%+-]+@[\w.-]+(\.[A-z]{2,5}){1,2}$
字符串的正则方法
exec()
- 在字符串中搜索匹配,返回一个结果数组或 null
matchAll()
- 可以一次性取出所有匹配,返回的是一个遍历器。正则表达式必须使用全局匹配模式
1
2
3
4
5const string = 'test1test2test3'
const regex = /t(e)(st(\d?))/g
for (const match of string.matchAll(regex)) {
console.log(match)
}
match()
- 从字符串中将符合正则表达式规则的内容中,返回数组或 null
- 默认,仅返回第一个匹配的结果 == exec()方法
- 使用全局匹配模式 g,返回与完整正则表达式匹配的所有结果 == matchAll()
test()
- 用来查看正则表达式与指定的字符串是否匹配,返回 true 或 false
search()
- 可以搜索字符串中是否含有指定内容,返回索引值
- 如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1
- 注:serach()只会查找第一个,即使设置全局匹配也没用
replace()
- 可以将字符串中指定内容替换为新的内容,返回字符串
- 参数:
- 被替换的内容,可以接受一个正则表达式作为参数
- 新的内容
split()
- 可以将一个字符串拆分为一个数组
- 如果传递一个空串作为参数,则会将每个字符都拆分为数组中的一个元素
修饰符
U 修饰符
- es6 新增,末尾加 u,代表 Unicode 模式(正确识别码点大于 0xFFFF 的 Unicode 字符)
i 修饰符 + u 修饰符
- 可以识别非规范的 K 字符
点字符 + u 修饰符
- 识别码点大于 0xFFFF 的 Unicode 字符
使用大括号表示 Unicode 字符,在正则表达式中必须加上 u 修饰符,否则大括号内容识别成量词
- 示例:
1
/\u{61}/u.test('a') // true
使用 u 修饰符,量词会正确识别 Unicode 字符
/^\S$/u.test(‘𠮷’)
- /S 匹配所有非空白字符,加了 u,能正确匹配码点大于 0xFFFF 的 Unicode 字符
RegExp.prototype.unicode 属性
- 判断是否加了 u 修饰符
y修饰符
- 『全局匹配』,后一次匹配都从上一次匹配成功的下一个位置开始[『必须从剩余的第一个位置开始』]
- reg.lastIndex: 指定从那个位置开始匹配,但『必须从这个位置发现匹配』
- reg.sticky: 表示是否设置了 y 修饰符。
- reg.flags: 返回正则表达式的修饰符
- 示例:
1
2
3
4
5
6const s = 'aaa_aa_a'
/a+/g.exec(s) // 第一回:aaa,第二回:aa
/a+/y.exec(s) // 第一回:aaa,第二回:null
// y修饰符+ g修饰符 :已数组的形式返回所有符合y修饰符规则的元素
'a1a2a3'.match(/a\d/y) // ["a1"]
'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"]
修饰符 g 与修饰符 y 区别:
- g:可以在剩余的位置中只要找到对应的内容,就匹配返回
- y:必须从剩余第一位匹配,如果第一位不是就返回 null
v 修饰符:属性类的运算
- 前提: 正则表达式必须使用 v 修饰符。
- 差集运算(A 减去 B):
[A--B]
- 交集运算(A 与 B 的交集):
[A&&B]
- 示例:
1
[\p{Decimal_Number}--[0-9]] // 十进制去除 ASCII 码的 0 到 9
d修饰符: 正则匹配索引
- 让 exec()、match() 的返回结果『添加indices』属性,在该属性上可以拿到『匹配的开始位置和结束位置』
- 注:开始位置包含在匹配结果之中,相当于匹配结果的第一个字符的位置。但是,结束位置不包含在匹配结果之中,是匹配结果的下一个字符。
1
2
3
4
5const text = 'zabbcdef'
const re = /ab+(cd)/d
const result = re.exec(text)
// 匹配ab到cd的开始和cd的开始
result.indices // [ [ 1, 6 ], [ 4, 6 ] ]
Unicode 属性类
- 默认是 『\u{…}』
- es2018,『\p{…}』:匹配满足条件的所有字符。(加上 u 修饰符)
- es2018,『\P{…}』:是『\p{…}的反向匹配』,即匹配不满足条件的所有字符。(加上 u 修饰符)
- 标准形式:
1
\p{UnicodePropertyName=UnicodePropertyValue}
- 可以只写属性名或属性值
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13// \p{Number}匹配所有数组,包括罗马数字。
const regex = /^\p{Number}+$/u;
// 匹配所有空格
\p{White_Space}
// 匹配各种文字的所有字母,等同于 Unicode 版的 \w
[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配 Emoji
/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
// 匹配所有的箭头字符
const regexArrows = /^\p{Block=Arrows}+$/u;
regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
具名组匹配
- 在圆括号内部,模式的头部添加
1
2语法: 问号 + 尖括号 + 组名
eg: ?<组名> - 然后在 exec方法 返回结果的 groups 属性上引用该组名。
- 『数字序号』引用(matchObj[1])依然『有效』
- 如果具名组『没有匹配』,那么『对应的groups』对象属性会是undefined,但『键名』再 groups 始终『存在』
- 在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法
- 示例:
1
2
3
4
5
6
7const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const matchObj = RE_DATE.exec('1999-12-31')
const year = matchObj.groups.year // 1999
const month = matchObj.groups.month // 12
const day = matchObj.groups.day // 31
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/
RE_TWICE.test('abc!abc!abc') // true
解构赋值和替换
- 字符串替换时,使用$<组名>引用具名组。
- 示例:
1
2let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
遍历器转为数组
- 方法一:
1
[...string.matchAll(regex)]
- 方法二:
1
Array.from(string.matchAll(regex))