正则表达式
正则表达式是描述一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
我们判断字符串是否含有子串,我们一般会用到indexOf 还有includes方法, 比如 "abc".includes("ab") 或者 "abc".indexOf(ab), 那么这两个有什么区别?
includes 返回的是Boolean值;
而indexOf 返回的是子串在字符串首次出现的下标,若没有出现返回-1
除了以上两种方法,我们还有正则表达式,/ab/.test("abc")
先来看几个常用特殊的字符
*代表前面的字符0次或多次, 例子/ac*/.test("abc")
说明
ac能匹配“a”,也能匹配“ac”以及“acc”。等价于{0,}。
+代表前面的字符1次或多次,/ac+/.test("abc")
说明
ac+ 能匹配“ac”以及“acc”, 但不能匹配”a”, +等价于{1,}。
?代表前面的字符0次或1次, 例子/ac?/.test("abc")
例如,“do(es)?”可以匹配“do”或“does”。?等价于{0,1}。/d匹配数字\w匹配字母、数字、下划线。等价于 [A-Za-z0-9_]\s匹配所有空白符,包括换行\S匹配非空白符,不包括换行。[\s\S]匹配所有
基础
字符组 []
字符组:允许匹配一组可能出现的字符。比如 [Jj], 那么既可以匹配J,也能匹配j, 例子/[Jj]ava[Ss]cript/.test("JavaScript")
区间
在字符组中用-代表区间,即[0-9]表示的是[0123456789], 同理[a-z]表示的是a到z任意的数字
字符转义
上面所说,[]表示字符组, 而- 表示区间连接符,那么如果要匹配[] 那么就需要使用转义符 \,
答案
/\[\]/.test("[]")
取反 [^]
true 取反 就是false, 在正则这里的取反是指 不会出现的字符
比如: 匹配不包含数字的字符串 /[^0-9]/.test("abc"),
注意:^符号要在中括号内,不然^代表的是匹配以^后跟随的的字符开头的字符串,/^gg/.test("gg = good game"), 这里表示的是以gg开头的匹配式。
这里顺带说一下 $, 表示以$前面的字符组做结尾的匹配式 /world$/.test("hello world")
不含小写字母的数据
`/[^a-z]/.test("0123123ADFADJFKL")`重复
在一个字符组后加上{N} 就可以表示在它之前的字符组出现N次。例如 /\d{3}/ ,表示数字重复3次
假设要匹配 电话号码,那么应该怎么做, 电话号码的格式是020-12345678
匹配电话号码
`/\d{3}-\d{8}/`重复区间 {M,N}
可能有时候,我们不知道具体要匹配字符组要重复的次数,比如身份证有15位也有18位的。那么这时候就可以用重复区间,
语法:{M,N},M是下界而N是上界。
练习 匹配所有的手机号
我们知道手机号必须为11位数,并符合下列几个规则:
第一位数字必须以1开头,第二位数字可以是[3,4,5,7,8]中的任意一个,后面9个数是[0-9]中的任意一个数字
匹配所有的手机号
`/1[34578]\d{9}/`贪婪匹配与非贪婪匹配
贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配 (*);
非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配 (?);
例子: <div>12312</div><div>hello world</div>
贪婪匹配: "<div>12312</div><div>hello world</div>".match(/<div>.*</div>/), 会匹配到 <div>12312</div><div>hello world</div>
非贪婪匹配: "<div>12312</div><div>hello world</div>".match(/<div>.*?</div>/), 会匹配到 <div>12312</div>, 这是因为非贪婪匹配,匹配到第一个子串就结束了,不再往后匹配了
分组
分组:使用的是(),分组一般用在提取匹配的字符串的子串。 举个例子: 020-12345678 这是我们的电话号码,但我想提取他的区号和真实的电话号码,那么我们只需要/(\d{3})-(\d{8})/即可
练习1: 如果我想要提取<div>hello world</div>
答案
"<div>hello world</div>".match(/<div>(.*?)<\/div>/)
比较:
1. <div>.*?</div>
2. <div>(.*?)</div>练习2: 如果 我们拥有日期的格式为 2020-05-20 或者 2020 05 20 或者 2020/05/20 这样的格式,我们想要获取他的年月日 应该怎么写
答案
/(\d{4})[\-\s\/](\d{2})[\-\s\/](\d{2})/测试一下
"2020-05-20".match(/(\d{4})[\-\s\/](\d{2})[\-\s\/](\d{2})/)
中间分隔符是[\-\s\/], 我们换成[\s\S]是否也可以?那如果日期的格式是 2020-5-20 或者是 2021-2-2 那应该怎么改造他
答案
/(\d{4})[-](\d{1,2})[-](\d{1,2})/非捕获分组
有时候,我们并不需要捕获某个分组的内容,但是又想使用分组的特性。即我不要这个分组。
非捕获分组: (?:表达式)
例子: 现在有 电话号码 020-12345678 或者tel:12345678, 我只要要他的电话号码,那么(?:\d{3}|tel)[-:](\d{8})
分组回溯引用
分组回溯引用的意思就是,我能使用之前匹配的分组, 用\1表示第一个分组, \2表示第二个分组,如此类推
例如,要匹配一段 HTML 代码,比如:<font>hello world</font>,我们会写成 /<\w+>.*?<\/\w+>/, 这样能匹配<font>hello world</font>, 但是如果数据改成这样:<font>hello world</bar>, font 和 bar 不是一对正常的标签,所以上面的表达式不太正确, 这个时候就可以使用分组回溯引用, /<(\w+)>.*?<\/\1>/
练习: 如果要匹配符合 ab ba 这种关系的单词, 应该怎么写?
匹配abba, asffs
答案
(\w+)(\w+)\2\1正向先行断言
正向先行断言:(?=表达式),指在某个位置向右看,表示所在位置右侧必须能匹配表达式,但匹配的表达式不会出现在结果组里面
例如:
我喜欢你 我喜欢 我喜欢我 喜欢 喜欢你
如果要取出喜欢两个字,要求这个喜欢后面有你,这个时候就要这么写:喜欢(?=你),这就是正向先行断言。
看这段正则表达式(?=.*?[a-z])(?=.*?[A-Z]).+, 这段正则能够匹配包含至少一个大小写字母的字符串, 把正则拆开就是
(?=)说明是后面必须是什么东西,.*?表示任意东西[a-z]表示a-z的小写字母
结合就是 必须包含a到z的任意字符串
练习: 密码强度验证规则如下:
- 至少一个大写字母
- 至少一个小写字母
- 至少一个数字
- 至少8个字符
答案
/(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9]).{8}/反向先行断言
反向先行断言:(?!表达式)的作用是保证右边不能出现某字符。
用上面的例子就是,如果要取出喜欢两个字,要求这个喜欢后面没有你,这个时候就要这么写:喜欢(?!你),这就是反向先行断言。
我觉得:正向先行断言跟反向先行断言基本类似,只是一个取反的操作
练习: 排除qq邮箱
答案
/@(?!qq).*/@后面没有qq用正向现行断言以及反向现行断言 千分位格式化数字
"100000".replace(/\B(?=(\d{3})+(?!\d))/g, ',')
解析: /\B(?=(\d{3})+(?!\d))
\B, 首先先理解\b, 在字符串中"here is a word", 在这个字符串中其实用很多\b, 就是单词与单词之间有\b,所以如果用/\bhere\b/可以匹配到here这个单词,但如果字符串是hereisaword, 这样就匹配不了here了,我们可以简单是想象成空格符。\B是只非字间, 和\d与\D,\w与\W的取反关系一样(?=(\d{3})): 先分组,3个数字为一组,加正向先行断言也就是 必须包含3个数字为一组的字符串+一次或多次(?!\d): 反向先行断言,后面必须不能包含数字- 结合就是
3个数字为一组的子串后面必须没有数字
正向后行断言
先行断言和后行断言只有一个区别,即先行断言从左往右看,后行断言从右往左看。
正向后行断言:(?<=表达式),指在某个位置向左看,表示所在位置左侧必须能匹配表达式
例如:如果要取出喜欢两个字,要求喜欢的前面有我,后面有你,这个时候就要这么写:(?<=我)喜欢(?=你)。
反向后行断言
反向后行断言:(?<!表达式),指在某个位置向左看,表示所在位置左侧不能匹配表达式
用上面的例子就是, 我喜欢你, 喜欢前面没有我
例子: 匹配一个 $ 符号: (?<!\$)\$[^\$]*\$(?!.)
解析:
(?<!\$)\$:$前面没有$\$(?!$):$的后面没有$[^\$]*: 除了$的任意字符
应用
- 日期转化, 获取年月日,时分秒
- 获取url中的参数
- trim() 函数
- 模板
function getUrlParams(url) {
const pattern = new RegExp(/(\w+)=(\w+)/, 'gi')
let res = {};
str.replace(reg, (match, p1, p2) => {
res[p1] = p2;
return `${p1}=${p2}`
})
return res;
}
getUrlParams("https://www.baidu.com?username=admin&password=88888888&code=1234")
trim()
function trim(str, type: "left" | "right" | "both" | "all") {
if (type === 'left') {
return str.replace(/(^\s*)/g, "");
}
if (type === 'right') {
return str.replace(/(\s*$)/g, "")
}
if (type === 'both') {
return str.replace(/(^\s*)|(\s*$)/g, "");
}
return str.replace(/(\s*)/g, "");
}
简易模板引擎
const data = {name: 'Bill', age: 111};
const template = `
my name is {{name}}
年龄 {{age}}
`
function generate(template, mapData) {
const reg = new RegExp(/\{\{(.*?)\}\}/, "g");
const res = template.replace(reg, (match, p1) => mapData[p1])
return res;
}
const a = generate(template, data);
document.body.innerHTML += a
…完