我们先来看一下维基百科对正则表达式的解释:
✅ 正则表达式(英语:Regular Expression,常简写为regex、regexp或RE),又称正则表示式、正则表示法、规则表达式、常规表示法,是计算机科学的一个概念;
简单概况:正则表达式是一种字符串匹配利器,可以帮助我们搜索、获取、替代字符串;
在JavaScript中,正则表达式使用RegExp类来创建,也有对应的字面量的方式:
正则表达式主要由两部分组成:模式(patterns)和修饰符(flags)。
const reg1 = new RegExp('aaaa', 'i');
const reg2 = /aaaa/i;
✅ JavaScript中的正则表达式被用于 RegExp 的 exec 和 test 方法;
✅ 也包括 String 的 match、matchAll、replace、search 和 split 方法;
方法 | 描述 |
---|---|
exec | 一个在字符串中执行查找匹配的 RegExp 方法,它返回一个数组(未匹配到则返回 null)。 |
test | 一个在字符串中测试是否匹配的 RegExp 方法,它返回 true 或 false。 |
match | 一个在字符串中执行查找匹配的 String 方法,它返回一个数组,在未匹配到时会返回 null。 |
matchAll | 一个在字符串中执行查找所有匹配的 String 方法,它返回一个迭代器(iterator)。 |
search | 一个在字符串中测试匹配的 String 方法,它返回匹配到的位置索引,或者在失败时返回-1。 |
replace | 一个在字符串中执行查找匹配的 String 方法,并且使用替换字符串替换掉匹配到的子字符串。 |
split | 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中 的 String 方法。 |
常见的修饰符:
flag | 含义 |
---|---|
g | 全部的,给我匹配全部的(global) |
i | 忽略大小写(ignore) |
m | 多行匹配(multiple) |
❗ 需求: 1️⃣ 获取一个字符串中所有的abc;2️⃣ 将一个字符串中的所有abc换成大写;
let message = "Hello ABC,abc,Abc,AAaBC";
const pattern = /abc/ig;
const result = message.match(pattern);
console.log(result);
message = message.replaceAll(pattern, 'ABC');
console.log(message)
字符类(Character classes) 是一个特殊的符号,匹配特定集中的任何符号。
字符 | 含义 |
---|---|
\d (“d"来自"digit”) | 数字:从0到9的字符。 |
\s (“s"来自"space”) | 空格字符:包括空格,制表符 \t,换行符 \n 和其他少数稀有字符,例如\v \f \r。 |
\w (“w"来自"word”) | “单字”字符:拉丁字母或数字或下划线 _ 。 |
. (点) | 点 . 是一种特殊字符类,它与“除换行符之外的任何字符”匹配 |
反向类 (Inverse classes)
符号 ^ 和符号 $ 在正则表达式中具有特殊的意义,它们被称为“锚点”。
✅ 符号 ^ 匹配文本开头;
✅ 符号 $ 匹配文本末尾;
词边界(Word boundary)
词边界 \b 是一种检查,就像 ^ 和 $ 一样,它会检查字符串中的位置是否是词边界。
词边界测试 \b 检查位置的一侧是否匹配 \w,而另一侧则不匹配 “\w”
在字符串 Hello, Java! 中,以下位置对应于 \b:
如果要把特殊字符作为常规字符来使用,需要对其进行转义:
❗ 只需要在它前面加个反斜杠;
✅ 常见的需要转义的字符:
[] \ ^ $ . | ? * + ( )
斜杠符号 ‘/’ 并不是一个特殊符号,但是在字面量正则表达式中也需要转义;
练习:匹配所有以.js或者jsx结尾的文件名
const fileNames = ["abc.js", "cba.java", "nba.html", "mba.js", "aaa.jsx"];
let reg = /\.jsx?$/;
const newNames = fileNames.filter((item) => {
return reg.test(item)
})
console.log(newNames);
在webpack当中,匹配文件名时就是以这样的方式。
◼ 有时候我们只要选择多个匹配字符的其中之一就可以:
在方括号 […] 中的几个字符或者字符类意味着“搜索给定的字符中的任意一个”;
◼ 集合(Sets)
比如说,[eao] 意味着查找在 3 个字符 ‘a’、‘e’ 或者 `‘o’ 中的任意一个;
◼ 范围(Ranges)
方括号也可以包含字符范围;
比如说,[a-z] 会匹配从 a 到 z 范围内的字母,[0-5] 表示从 0 到 5 的数字;
[0-9A-F] 表示两个范围:它搜索一个字符,满足数字 0 到 9 或字母 A 到 F;
\d —— 和 [0-9] 相同;
\w —— 和 [a-zA-Z0-9_] 相同;
案例:匹配手机号码
const phonePattern = /^1[356789]\d{9}$/
◼ 排除范围:除了普通的范围匹配,还有类似 [^…] 的“排除”范围匹配;
[^0-9]
:排除数字0-9的范围
◼ 假设我们有一个字符串 +7(903)-123-45-67,并且想要找到它包含的所有数字。
◼ 数量 {n}
◼ 缩写:
**案例:匹配开始或结束标签 **
const htmlElement = "哈哈哈哈
heiheih2
";
const tagPatter = /<\/?[a-z][a-z0-9]*>/ig;
console.log(htmlElement.match(tagPatter))
如果我们有这样一个需求:匹配下面字符串中所有使用《》包裹的内容
默认情况下的匹配规则是查找到匹配的内容后,会继续向后查找,一直找到最后一个匹配的内容
懒惰模式中的量词与贪婪模式中的是相反的。
const message = "我最喜欢的两本书:《家》、《春秋》";
// 默认 .+ 采用贪婪模式
// const reg = /《.+》/ig;
// 使用懒惰模式
const reg = /《.+?》/ig;
const result = message.match(reg);
console.log(result)
模式的一部分可以用括号括起来 (…),这称为“捕获组(capturing group)”。
◼ 这有两个作用:
◼ 方法 str.match(regexp)
,如果 regexp 没有 g
标志,将查找第一个匹配并将它作为一个数组返回:
…等等…
案例:匹配到《》,并且获取其中的内容
const message = "我最喜欢的两本书:《家》、《春秋》、《黄金时代》";
const reg = /《(.+?)》/ig;
const resultIteraor = message.matchAll(reg);
for (const item of resultIteraor) {
console.log(item[1])
}
命名组:
非捕获组:
or是正则表达式中的一个术语,实际上是一个简单的“或”。
// 解析歌词
// http://123.207.32.32:9002/lyric?id=167876
const lyricString = "[00:00.000] 作词 : 许嵩\n[00:01.000] 作曲 : 许嵩\n[00:02.000] 编曲 : 许嵩\n[00:03.000] 制作人 : 许嵩\n[00:22.240]天空好想下雨\n[00:24.380]我好想住你隔壁\n[00:26.810]傻站在你家楼下\n[00:29.500]抬起头数乌云\n[00:31.160]如果场景里出现一架钢琴\n[00:33.640]我会唱歌给你听\n[00:35.900]哪怕好多盆水往下淋\n[00:41.060]夏天快要过去\n[00:43.340]请你少买冰淇淋\n[00:45.680]天凉就别穿短裙\n[00:47.830]别再那么淘气\n[00:50.060]如果有时不那么开心\n[00:52.470]我愿意将格洛米借给你\n[00:55.020]你其实明白我心意\n[00:58.290]为你唱这首歌没有什么风格\n[01:02.976]它仅仅代表着我想给你快乐\n[01:07.840]为你解冻冰河为你做一只扑火的飞蛾\n[01:12.998]没有什么事情是不值得\n[01:17.489]为你唱这首歌没有什么风格\n[01:21.998]它仅仅代表着我希望你快乐\n[01:26.688]为你辗转反侧为你放弃世界有何不可\n[01:32.328]夏末秋凉里带一点温热有换季的颜色\n[01:41.040]\n[01:57.908]天空好想下雨\n[01:59.378]我好想住你隔壁\n[02:02.296]傻站在你家楼下\n[02:03.846]抬起头数乌云\n[02:06.183]如果场景里出现一架钢琴\n[02:08.875]我会唱歌给你听\n[02:10.974]哪怕好多盆水往下淋\n[02:15.325]夏天快要过去\n[02:18.345]请你少买冰淇淋\n[02:21.484]天凉就别穿短裙\n[02:22.914]别再那么淘气\n[02:25.185]如果有时不那么开心\n[02:27.625]我愿意将格洛米借给你\n[02:30.015]你其实明白我心意\n[02:33.327]为你唱这首歌没有什么风格\n[02:37.976]它仅仅代表着我想给你快乐\n[02:42.835]为你解冻冰河为你做一只扑火的飞蛾\n[02:48.406]没有什么事情是不值得\n[02:52.416]为你唱这首歌没有什么风格\n[02:57.077]它仅仅代表着我希望你快乐\n[03:01.993]为你辗转反侧为你放弃世界有何不可\n[03:07.494]夏末秋凉里带一点温热\n[03:11.536]\n[03:20.924]为你解冻冰河为你做一只扑火的飞蛾\n[03:26.615]没有什么事情是不值得\n[03:30.525]为你唱这首歌没有什么风格\n[03:35.196]它仅仅代表着我希望你快乐\n[03:39.946]为你辗转反侧为你放弃世界有何不可\n[03:45.644]夏末秋凉里带一点温热有换季的颜色\n";
const lyricArr = [];
const lyricStringsArr = lyricString.split('\n');
const lyricTimeRe = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/;
for (const item of lyricStringsArr) {
const timeArr = item.match(lyricTimeRe);
if (!timeArr) continue
const min = timeArr[1] * 60 * 1000;
const second = timeArr[2] * 1000;
const minSecond = timeArr[3].length === 3 ? timeArr[3] * 1 : timeArr[3] * 10;
const time = min + second + minSecond
const lyricContent = item.replace(lyricTimeRe, "").trim();
lyricArr.push({ time, lyricContent })
}
console.log(lyricArr);
时间格式化:从服务器拿到时间戳,转成想要的时间格式
// 时间格式化
// 时间格式化:从服务器拿到时间戳,转成想要的时间格式
function formatDate(time, timeFormat) {
const date = new Date(time);
console.log(date);
const week = ["日", "一", "二", "三", "四", "五", "六"];
const regObj = {
"y+": date.getFullYear(),
"M+": date.getMonth() + 1,
"d+": date.getDay(),
"h+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds(),
"w": week[date.getDay()]
}
for (const key in regObj) {
const keyReg = new RegExp(key, 'i');
if (keyReg.test(timeFormat)) {
// const value = (regObj[key] + "").padStart(2, "0");
let value = regObj[key];
if (typeof (value) === 'number') {
value = (value + "").padStart(2, "0")
}
timeFormat = timeFormat.replace(keyReg, value)
}
}
return timeFormat
}
const obj = {
name: 'lllll',
time: Date.now()
}
const time = obj.time;
let result = formatDate(time, 'yyyy年MM月dd日 h:mm:ss 星期w');
console.log(result);
// console.log(formatDate(new Date(1409894060000), 'yyyy-MM-dd HH:mm:ss 星期w'))