正则表达式
正则表达式(英语:
Regular Expression,常简写为regex、regexp或RE),又称正则表示式、正则表示法、规则表达式、常 规表示法,是计算机科学的一个概念;
- 正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
- 许多程序设计语言都支持利用正则表达式进行字符串操作。
- 简单概况:正则表达式是一种字符串匹配利器,可以帮助我们搜索、获取、替代字符串;
1、JavaScript中创建正则表达式的方式
Section titled “1、JavaScript中创建正则表达式的方式”-
构造器的方式
//注意: 补充一下 “\” 才是转义符 “/” 不是const regexp = new RegExp("规则", "修饰符")new RegExp('ab+c', 'i'); // 首个参数为字符串模式的构造函数new RegExp(/ab+c/, 'i'); // 首个参数为常规字面量的构造函数 -
字面量的方式
const regexp = /"规则"/"修饰符"
2、regexp修饰符
Section titled “2、regexp修饰符”| flag | 含义 |
|---|---|
| g | 全部的,不使用g修饰符的话只会匹配第一次出现的 |
| i | 忽略大小写 |
| m | multiple 多行匹配,当存在 /n 换行符的时候。添加限制 例:this an/n on regexp = /an$/. 如果不使用 m 修饰符的话,就会认为 是以 an 结尾的。 |
3、规则-字符类
Section titled “3、规则-字符类”字符类(Character classes) 是一个特殊的符号,匹配特定集中的任何符号。
Section titled “字符类(Character classes) 是一个特殊的符号,匹配特定集中的任何符号。”| 字符 | 含义 |
|---|---|
| \d(“d” 来自 “digit”) | 数字:从 0 到 9 的字符。 |
| \s(“s” 来自 “space”) | 空格符号:包括空格,制表符 \t,换行符 \n 和其他少数稀有字符,例如 \v,\f 和 \r。 |
| \w(“w” 来自 “word”) | “单词”字符:拉丁字母 或 数字 或 下划线 _。 |
| .(点) | 指**\n 除外的所有字符** |
反向类(Inverse classes)
Section titled “反向类(Inverse classes)”| 字符(大写) | 含义(小写) |
|---|---|
| \D 非数字 | 除 \d 以外的任何字符,例如字母。 |
| \S 非空格符号 | 除 \s 以外的任何字符,例如字母。 |
| \W 非单字字符 | 除 \w 以外的任何字符,例如非拉丁字母或空格。 |
4、规则 – 锚点(Anchors)
Section titled “4、规则 – 锚点(Anchors)”^: 符号 ^ 匹配文本开头$: 符号 $ 匹配文本末尾;
-
注意:两边同时都加了锚点的话就代表匹配的字符串 开头结尾中间都要相等
-
结合使用规则:必须要是 ‘开头’, ‘结尾’, ‘中间’都要是 WYN的字符,
//例 匹配的字符串必须是 “wyn”if(/^wyn$/ig.test("wyn")) {console.log("ok")} else {console.log("fail")}//规则:必须要是 ‘开头’, ‘结尾’, ‘中间’都要是 WYN的字符,
词边界(Word boundary)(1)
Section titled “词边界(Word boundary)(1)”-
词边界
\b位置的一边不能包含\w【数字字母下划线】就是使用词边界的一侧不能出现 数字,字母,下划线,但可以是空格 ! 不符合
\w的 -
注意:\w 是单词字母,数字,和下划线,不包括字符
5、规则 – 转义字符串
Section titled “5、规则 – 转义字符串”如果要把特殊字符作为常规字符来使用,需要对其进行转义:
- 只需要在它前面加个反斜杠;
常见需要转义的字符
Section titled “常见需要转义的字符”[ ] \ ^ $ . | ? * + ( )
- 注意: 斜杠符号 ‘/’ 并不是一个特殊符号,但是在字面量正则表达式中也需要转义;
- 使用
\转义
6、集合(Sets)和范围(Ranges)
Section titled “6、集合(Sets)和范围(Ranges)”可以设置一个范围,匹配的字符只需要该范围内其中一个就可以
1. 集合(Sets)
Section titled “1. 集合(Sets)”方括号 […] 中的几个字符或者字符类意味着“搜索给定的字符中的任意一个”;
//效果都是一样的/[12345]/ig ,/[1,2,3,4,5]/ig, /[1|2|3|4|5]/ig2. 范围(Ranges)
Section titled “2. 范围(Ranges)”在集合[ ] 方括号中使用 “-” 可以表示一个范围
-
例:[a-z] 会匹配从 a 到 z 范围内的字母,[0-5] 表示从 0 到 5 的数字;
-
表示多个范围:[0-9A-F] 表示两个范围:它搜索一个字符,满足数字 0 到 9 或字母 A 到 F;
const num = 123;console.log(/[110-130]/ig.test(num));
3. 排除范围
Section titled “3. 排除范围”除了普通的范围匹配,还有类似 [^…] 的“排除”范围匹配;
- 在范围的前面添加 ‘^’ 表示排除当前范围
\d -> [0-9] \D -> [^0-9]7、量词(Quantifiers)
Section titled “7、量词(Quantifiers)”用来表示当前 regexp 要作用在几个字符的上面
数量 {n}
Section titled “数量 {n}”-
确切的位数:
{5}表示会有5个a,来满足当前正则
const regexp = /a{3,5}/igm // 表示3 ~ 5 之间 -
某个范围的位数:
{3,5}- 注意:这里的 ,两边不允许添加空格
-
{1, }表示的是 1 个或者多个 和 + 号的作用表示相同的作用同样{0,} 表示的做用和 * 号相同
缩写 + ?*
Section titled “缩写 + ?*”+代表 1 到多个*代表 0 到多个?0个或者一个, 就是有没有都可以
修饰前面的正则
//案例: 字符串的html元素, 匹配出来里面所有的标签 const htmlElement = "<div><span>哈哈哈</span><h2>我是标题</h2></div>" console.log(htmlElement.match(/<\/?[a-z][a-z0-9][a-z]?[a-z]*>/ig)) //这里的?代表了[a-z]存在0个或者1个都可以8、贪婪( Greedy)和惰性( lazy)模式
Section titled “8、贪婪( Greedy)和惰性( lazy)模式”默认情况下、正则表达式是贪婪模式,在获取一个区间的字符串,会获取满足最后一个和第一个出现的正则规则的符号之间的字符
const message = "我最喜欢的两本书: 《黄金时代》和《沉默的大多数》、《一只特立独行的猪》"console.log(message.match(/《.+》/ig)) //返回所有匹配的数组-
这样的正则返回的是
[《黄金时代》和《沉默的大多数》、《一只特立独行的猪》]在第一个和”《 ” 最后一个 ”》” 之间的所有字符,返回一个元素
-
而返回的
[《黄金时代》,《沉默的大多数》,《一只特立独行的猪》]而我们想要的是这种效果,就要切换为 lazy 模式
启用lazy 模式
Section titled “启用lazy 模式”-
我们可以在量词后面再加一个问号 ‘?’ 来启用它;
-
所以匹配模式变为 ? 或 +?,甚至将 ’?’ 变为 ??*
注意:这里一定要放在量词的后面
Section titled “注意:这里一定要放在量词的后面”不放在量词的后面表示无效
9、捕获组(capturing group)
Section titled “9、捕获组(capturing group)”模式的一部分可以用括号括起来 (…),这称为“捕获组(capturing group)”。
-
它将括号视为一个整体;
//表示abc的c有一到多个的字符串,至少存在一个info.match(/(abc+){1,}/ig)这里将 abc 当成一个单独项的字符来匹配,不加小括号分组的话表示的是 c 有 1 或者多个
-
它允许将匹配用括号括起来的一部分作为matchAll结果数组中的单独项,按照索引顺序排布;
对于更复杂的模式,计算括号很不方便。给括号(当前组)起个名字。
(?<自定义命名>)
const message = "我最喜欢的两本书: 《黄金时代》和《沉默的大多数》、《一只特立独行的猪》"message.matchAll(/《(?<wyn>.+?)》/ig).next()-
命名组会放在数组中的
groups属性里,可以通过groups属性获取对应的分组数据[0: "《黄金时代》"1: "黄金时代"//存放命名组数据groups: {wyn: '黄金时代'}index: 10input: "我最喜欢的两本书: 《黄金时代》和《沉默的大多数》、《一只特立独行的猪》"length: 2]
3.非捕获组-(排除组)
Section titled “3.非捕获组-(排除组)”用来排除一个捕获组
-
当使用场景需要捕获组(括号)来表达一组应用量词,并且不希望它们的内容出现在结果中
-
在捕获组内的开头添加
?:来排除组。console.log(info.match(/.(?:abc)/i));
or是正则表达式中的一个术语,实际上是一个简单的“或”。
-
在正则表达式中,它用竖线
|表示; -
通常会和捕获组一起来使用,在其中表示多个值;
(123| 234 | 234) 和 | 在一起的时候也表示允许匹配多个值,与 [123] 的区别就是 集合表示的是多个单一的字符
5、重点注意:match 返回的类数组对象 0 索引是当前匹配的字符串
10、Regular Expression 的使用
Section titled “10、Regular Expression 的使用”1. 实例方法 2个
Section titled “1. 实例方法 2个”regexp.test()
Section titled “regexp.test()”返回一个 boolean 值,正常匹配到返回true 否则返回 false
regexp.exec()
Section titled “regexp.exec()”一个在字符串中执行查找匹配的 RegExp 方法,它返回一个数组
-
注意:使用全局修饰符的时候,同一个 regexp 实例要再次调用才会返回下一次的匹配
相当于是一个迭代器
['i', index: 10, input: 'zhangsan lisi wangwu', groups: undefined]//匹配到的字符0: "i"//分组属性groups: undefined//索引属性index: 10//整体字符串input: "zhangsan lisi wangwu"//length 属性length: 1[[Prototype]]: Array(0)2. String中可以使用正则的方法(5个)
Section titled “2. String中可以使用正则的方法(5个)”简但理解:match 主要返回的是对象,如果单一加g 或者多不加g都会返回不一样的效果
str.match
Section titled “str.match”- 一个在字符串中执行查找匹配的
String方法,它返回一个数组 包含匹配的所有字符,在未匹配到时会返回null。 - 注意:这里使用全局描述符 g 话会返回单一字符串的结果数组
(就是普通字符串数组),不加 g 的话返回的是 详细数据信息(有分组信息的)数组
str.matchAll
Section titled “str.matchAll”- 一个在字符串中执行查找所有匹配的 String 方法,它返回一个迭代器(iterator)
- 正则表达式不能省略全局修饰符 g 会报错,
- 迭代器 value 是个数组 同 regexp.exec() 返回的数组
- 迭代器也可以遍历 for…of
str.search
Section titled “str.search”返回第一个匹配字符的索引 所以加不加 g 都无所谓
str.replace
Section titled “str.replace”-
replace和replaceAll的区别就是在正则表达式中添加了 g 修饰符且
replaceAll参数如果是正则必须要添加g修饰符否则报错//这里的 regexp 可以不加 g 修饰符const temp = str.replaceAll(/cba/ig, "nba")//replaceAll这里的 regexp g 修饰符不加的话会报错 类型错误const newMessage2 = str.replaceAll(/\d+/ig, "")
str.split
Section titled “str.split”使用 regexp 来分割转换为数组
11、案例歌词格式化
Section titled “11、案例歌词格式化”- 将歌词文件的每句歌词转换为此格式 {time: 24380, content: ‘我好想住你隔壁’}
// 将歌词文件字符格式,以/n 分割转换为字符串const lyricList = arr.lrc.lyric.split(/\n/ig)//这里以一句歌词为例const newArr = ["[00:24.380]我好想住你隔壁"]
let regexp = /\[(\d{2,}):(\d{2,}).(\d{2,3})\]/ilyricList.forEach(el => { if(!el) return //不加 g 返回的是详细数组 let time = el.match(regexp) let minutes = time[1] * 60 * 1000 let second = time[2] * 1000 // 注意字符串的隐式转换,不然就是拼接了 time[3] * 1 要乘1 隐式转换number let millisecond = time[3].length === 2 ? time[3] * 10 : time[3] * 1 let content = el.replace(regexp, "") newArr.push({time: minutes + second + millisecond, content})});console.log(newArr)12、案例格式化时间工具(重点)
Section titled “12、案例格式化时间工具(重点)”- 将时间转换为指定格式的字符串
const formatString = "yyyy-MM-dd hh-mm-ss"
function formatData(Unix, formatString) { const data = new Data(Unix) //(重点) 指定规则设置对应的正则和时间值 let rule = { "y+": date.getFullYear(), "M+": date.getMonth() + 1, "d+": date.getDate(), "h+": date.getHours(), "m+": date.getMinutes(), "s+": String(date.getSeconds()).padStart(2, "0") } for (const key in rule) { if (Object.hasOwnProperty.call(rule, key)) { const element = rule[key]; let regexp = new RegExp(key) //用户设置了对应得格式。 if(regexp.test(formatString)) { //循环替换 formatString = formatString.replace(regexp, element) } } } return formatString }
console.log(formatData(new Date(), formatString))