基础
1. 零宽断言
| 写法 | 名称 | 含义 |
|---|---|---|
| exp1(?=exp2) | 正向前瞻 | 查找exp2前面的exp1 |
| (?<=exp2)exp1 | 负向前瞻 | 查找exp2后面的exp1 |
| exp1(?!exp2) | 正向后瞻 | 查找后面不是exp2的exp1 |
| (?<!exp2)exp1 | 负向后瞻 | 查找前面不是exp2的exp1 |
2.replace()方法与正则约定的特殊标记符$
| 写法 | 含义 |
|---|---|
| $i | (i:1-99),表示从左到右正则子表达式所匹配的文本 |
| $& | 表示与正则表达式匹配的全文本 |
| $` | `:切换技能键。表示匹配字符串左边的文本 |
| ? | 表示$转移 |
3. 分组语法:
| 写法 | 含义 |
|---|---|
| 捕获 | |
| (exp) | 匹配exp,并捕获到自动命名到组里 |
| (?exp) | 匹配exp,并捕获文本到名称为name到组里 |
| (?:exp) | 匹配exp,不捕获匹配的文本 |
| 位置指定 | |
| exp1(?=exp2) | 查找exp2前面的exp1 |
| (?<=exp2)exp1 | 查找exp2后面的exp1 |
| exp1(?!exp2) | 查找后面不是exp2的exp1 |
| (?<!exp2)exp1 | 查找前面不是exp2的exp1 |
| 注释 | |
| (?#comment) | 这种类型的组不对正则表达式对处理产生任何影响,只是为了提供让人阅读注释 |
4. 具有特殊意义的元字符
\d -> 匹配一个0-9的数字,相当于[0-9],和它相反的是\D ->匹配一个除了0-9的任意字符
\w -> 匹配一个0-9、a-z、A-Z、_的数字或字符,相当于[0-9a-zA-Z_]
\s -> 匹配一个空白字符(空格、制表符...)
\b -> 匹配一个单词的边界
\t -> 匹配一个制表符
\n -> 匹配一个换行
. -> 匹配一个除了\n以外的任意字符
^ -> 以某一个元字符开头
$ -> 以某一个元字符结尾
\ -> 转义字符
x|y -> x或者y的一个
[xyz] -> x、y、z中的任意一个
[^xyz] -> 除了xyz中的任意一个字符
[a-z] -> 匹配a-z中的任意一个字符
[^a-z] -> 匹配除了a-z中的任意一个字符
() -> 正则中的分组
()\1 -> 正则中的分组内容重复匹配一次,也就是()内的内容是重复一次显示的如:book oo就是重复匹配
5. 代表出现次数的"量词元字符"
->+ : 出现一到多次
->* : 出现零到多次
->? : 出现零到一次
->{n} : 出现n次
->{n,} : 出现n到多次
->{n,m} : 出现n-m次
6.?在正则中的五大作用:
- 左边是普通元字符,本身代表量词元字符出现0或者1次
- 左边是量词元字符,代表取消捕获时候的贪婪性
- (?:) 只匹配不捕获
- (?=) 正向预查
- (?!) 负向预查
7.()在正则中的三大作用
- 改变优先级
- 分组捕获
- 分组引用 \数字
8. 正则捕获
-
exec的依次执行结果,由lastIndex决定,match可以一次性匹配到所有的值,但是无法匹配分组的值,replace方法可以匹配到所有的值以及分组的值
-
匹配值:捕获到的结果是null或数组
- 第一项:本次捕获到的内容
- 其余项: 对应小分组本次单独捕获的内容
- index:当前捕获内容在字符串中的起始索引
- input: 原始字符串
-
懒惰性:如果正则为全局匹配,每次执行exec后如果有匹配结果,返回结果,并且该次匹配后lastIndex会修改成当前匹配内容最后一个字符的在原字符串中索引,如果没有匹配到结果,返回null,lastIndex索引会变成0,如下题目:
// 1.
var str = "duffy2016peixun2017";
var reg = /\d+/;
console.log(reg.lastIndex)
console.log(reg.exec(str));
console.log(reg.lastIndex)
console.log(reg.exec(str));
console.log(reg.lastIndex)
console.log(str.match(reg))
// 2.
var str = "duffy2016peixun2017";
var reg = /\d+/g;
console.log(reg.lastIndex)
console.log(reg.exec(str));
console.log(reg.lastIndex)
console.log(reg.exec(str));
console.log(reg.lastIndex)
console.log(reg.exec(str));
console.log(reg.lastIndex)
console.log(str.match(reg))
// 3.
const myRe = /ab*/g;
const str = 'abbcdefabh';
let myArray;
while ((myArray = myRe.exec(str)) !== null) {
let msg = `Found ${myArray[0]}. `;
msg += `Next match starts at ${myRe.lastIndex}`;
console.log(msg);
}
// 4.题目考查
let reg=/\d+/g;
let a;
while(a=reg.exec('sfsf2016sfs2017')){
console.log(a)
}
// 5.
let str = 'abcde123fgh456igk789lmn'
let reg = /\d+/
//reg.lastIndex: 当前正则下一次匹配的起始索引位置,这个值不会被修改,所以匹配到的永远是第一个
console.log(reg.lastIndex) //0 下面的匹配捕获是从str索引0的位置开始
//正则捕获的前提:当前正则要与字符串匹配。不匹配,捕获结果为null
console.log(reg.exec(str)) //["123", index: 5, input: "abcde123fgh456igk789lmn", groups: undefined]
reg = /\d+/g //设置全局匹配修饰符后,每一次匹配完,lastIndex会自动修改,多次捕获后,当全部捕获后,再次捕获结果为null,lastIndex回归为初始值0,再次捕获又从第一个开始
if(reg.test(str)){ //这里匹配后,已经修改了lastIndex的值
console.log(reg.exec(str)) //456 这里捕获的是第二个结果
}
//字符串中的match方法,可以在执行一次的情况下,捕获到所有匹配的数据(前提:正则设置了g)
console.log(str.macth(reg)) //["123", "456", "789"]
// 6.
let str = '3.1415'
let reg = /\d+(?!\.)/
reg.exec(str) // [1415] (1)
// 7. let str = 'abcd' let reg1 = /[a-c]+/ let reg2 = /[^d]$/ reg1.test(str) // true (1) reg2.test(str) // false (2)
-
分组捕获:
//既要匹配到{数字},也想单独匹配带数字 {0} 0...
let str = "{0}年{1}月{2}日"
//不设置g只匹配一次,exec和match获取的结果一致(既有大正则匹配的信息,也有小分组匹配的信息)
let reg = /\{(\d+)\}/ // ["{0}", "0", index: 0, input: "{0}年{1}月{2}日", groups: undefined]
let reg = /\{(\d+)\}/g
console.log(str.match(reg)) // ["{0}", "{1}", "{2}"] 全局匹配的情况下,match只能把大正则匹配的内容捕获到,小分组匹配的信息无法获取
let aryBig = [],
arySmall = [],
res = reg.exec(str) //用exec一次一次获取
while (res) { // 循环到res = null为止
let [big, small] = res
aryBig.push(big)
arySmall.push(small)
res = reg.exec(str) //没有捕获完就一直捕获
}
console.log(aryBig) //["{0}", "{1}", "{2}"]
console.log(arySmall) //["0", "1", "2"]
分组引用:()\1
// 1.
let str = "book" //要求一共4个字母,中间两个字母一样
let reg = /^[A-Za-z]([A-Za-z])\1[A-Za-z]$/
console.log(reg.test(str))
// 2.
let str = 'xuxi is xuxi is'
let reg = /(xuxi) (is) \1 \2/g
reg.test(str) // true (1)
str.replace(reg, '$1 $2') // xuxi is (2)
贪婪性: 默认情况下正则采用贪婪算法,按照当前正则匹配到的最长结果来获取,通过在量词元字符后设置?可以取消贪婪算法;
let str = 'aaa1111@2222bbb'
let reg = /\d+/g
console.log(str.match(reg)) //["1111", "2222"]
reg = /\d+?/g //["1", "1", "1", "1", "2", "2", "2", "2"]
-
test 实现捕获
let str = "{0}年{1}月{2}日"
let reg = /{(\d+)}/g
console.log(reg.test(str)) //true
console.log(typeof RegExp.$1) //"0"
console.log(reg.test(str)) //true
console.log(RegExp.$1) //"1"
console.log(reg.test(str)) //true
console.log(RegExp.$1) //"2"
console.log(reg.test(str)) //false
console.log(RegExp.$1) //"2" 存储的是上次捕获的结果
// RegExp.$1 ~ RegExp.$9 : 获取当前本次正则匹配后,第一个到第九个分组的信息
9. 字符串支持正则的方法
- search
let str = "hello world!";
console.log(str.search(/hello/) != -1);
// 只有一个参数, 并且是一个正则表达式对象, 如果传入一个非正则表达式对象,
// 则会使用 new RegExp(obj)隐式地将其转换为正则表达式对象
// 如果匹配成功, 则返回正则表达式在字符串中首次匹配项的索引, 否则, 返回-1
复制代码
- match
let str = "hello world!";
console.log(!!str.match(/hello/g));
// 如果传入一个非正则表达式对象, 则会隐式地使用new RegExp(obj)将其转换为一个RegExp
// 返回值(数组), 如果匹配到数组第一项是匹配的完整字符串, 之后项是用圆括号捕获的结果, 如果没有匹配到, 返回null
// 如果正则表达式包含g标志, 则该方法返回一个Array, 它包含所有匹配的子字符串而不是匹配对象
-
replace
// 1.
let str = 'abc000abc111' //把abc替换成abcxyz
str = str.replace('abc', 'abcxyz').replace('abc', 'abcxyz') //每一次捕获都是从字符串第一个位置开始找,类似于正则的懒惰性
console.log(str) // abcxyzxyz000abc111
str = str.replace(/abc/g, 'abcxyz')
console.log(str) // abcxyz000abcxyz111
// 2.
let time = '2021-09-11' //变成"2021年09月11日"
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/ //注意 {1,2} 逗号后面不能加空格,否则会匹配失败
console.log(reg.test(time)) //true
time = time.replace(reg, "$1年$2月$3日")
console.log(time) //2021年09月11日
- split
10. 词边界和非单词边界匹配
// 1.
let str = 'xuxi sa'
let reg1 = /\bsa\b/g
reg1.exec(str) // sa
let reg2 = /\bsa/g
reg2.exec(str) // sa
let reg3 = /sa\b/g
reg3.exec(str) // sa
let reg4 = /sa\B/g
reg4.exec(str) // null
let reg5 = /\Bsa/g
reg5.exec(str) // null
题目
- 字符串替换:
// 原字符串:
`<div id="app"><span class="app" src="app" name="app">class="app" </span><p attr="apps"></p></div>`
// 1.如何将上述字符串中id="app" 和 class="app"替换成id="root" 和 class="root"
`'<div id="root"><span class="root" src="app" name="app">class="app" </span><p attr="apps"></p></div>'`
// 代码:
'<div id="app"><span class="app" src="app" name="app">class="app" </span><p attr="apps"></p></div>'.replace(/(?<=(id|class)=")app(")(?!<|(\s+<))/g, 'root$2')
// 2.将上述字符串中每个dom节点,最后一个属性值为app的替换成root,如上述字符串替换后的结果为:
`<div id="root"><span class="app" src="app" name="root">class="app" </span><p attr="apps"></p></div>`
// 代码:
`<div id="app"><span class="app" src="app" name="app">class="app" </span><p attr="apps"></p></div>'.replace(/\bapp(")(?=>|(\s+>))/g, 'root$1')`
// 3.将上述字符串中每个dom节点,第一个属性值为app的替换成root,如上述字符串替换后的结果为:
'<div id="root"><span class="root" src="app" name="app">class="app" </span><p attr="apps"></p></div>'
// 代码:
`<div id="app"><span class="app" src="app" name="app">class="app" </span><p attr="apps"></p></div>'.replace(/(?<=<\w+\s+\w+=")app(?=")/g, 'root')`
// 4.将上述字符串中每个dom节点,每一个属性值为app的替换成root, 除name属性外,如上述字符串替换后的结果为:
'<div id="root"><span class="root" src="root" name="app">class="app" </span><p attr="apps"></p></div>'
// 代码:
`<div id="app"><span class="app" src="app" name="app">class="app" </span><p attr="apps"></p></div>'.replace(/(\b(?<!name)=")app(")(?!<|(\s+<))/g, '$1root$2')`
- 如何给一串数字用千分制表示?比如9999999999变成9,999,999,999
'99999999999.02'.replace(/\d{1,3}(?=(\d{3})+(?:\.\d+)?$)/g, '$&,')
输出:"99,999,999,999.02"
'99999999999.33333333'.replace(/\d{1,3}(?=(\d{3})+(?:\.\d+)?$)/g, '$&,')
输出:"99,999,999,999.33,333,333"
- 验证一个字符串中哪个字母出现的次数最多,有多少次
let str = 'jintianshixingqiliu'
let arr = str.split('')
arr = [...new Set(arr)]
let newArr = []
arr.forEach(item => {
let tmp = {
code: item
}
let i = 0
str = str.replace(new RegExp(item + "{1}", 'g'), () => i++)
tmp['num'] = i
newArr.push(tmp)
})
newArr.sort((a, b) => b.num - a.num)
console.log(newArr[0]) //{code: "i", num: 6}
let str = 'jintianshixingqiliu'
str = str.split('').sort((a, b) => a.localeCompare(b)).join('') //排序
let reg = /([a-zA-Z])\1+/g //匹配有相邻的相同的子字符串
let ary = str.match(reg)
ary.sort((a, b) => b.length - a.length)
console.log(ary[0].slice(0, 1), ary[0].length) //i, 6
let max = ary[0].length,
res = [ary[0].slice(0, 1)] // //可能有相同的最大数的字母
for (let i = 1; i < ary.length; i++) {
let item = ary[i]
if (item.length < max) {
break
}
res.push(item.slice(0, 1))
}
console.log(res)
let str = 'jintianshixingqiliu',
res = [],
max = 0,
flag = false //是否已经找到
str = str.split('').sort((a, b) => a.localeCompare(b)).join('')
for (let i = str.length; i > 0; i--) {
let reg = new RegExp("([a-zA-Z])\\1{" + (i - 1) + "}", "g")
str.replace(reg, (content, $1) => {
res.push($1)
max = i
flag = true
})
if (flag) break
}
console.log(res, max) //["i"] 6
- 去除字符串内指定元素的标签
function trimTag(tagName, htmlStr) {
let reg = new RegExp(`<${tagName}(\\s.*)*>(\\n|.)*<\\/${tagName}>`, "g")
return htmlStr.replace(reg, '')
}
- 短横线命名转驼峰命名
// 短横线转驼峰命名, flag = 0为小驼峰, 1为大驼峰
function toCamelCase(str, flag = 0) {
if(flag) {
return str[0].toUpperCase() + str.slice(1).replace(/-(\w)/g, ($0, $1) => $1.toUpperCase())
}else {
return str.replace(/-(\w)/g, ($0, $1) => $1.toUpperCase())
}
}
- 去除url参数字符串中值为空的字段
// 去除url参数字符串中值为空的字段
const trimParmas = (parmaStr:string = '') => {
return parmaStr.replace(/((\w*?)=&|(&\w*?=)$)/g, '')
}
- 实现搜索联想功能
function searchLink(keyword) {
// 模拟后端返回数据
let list = ['abc', 'ab', 'a', 'bcd', 'edf', 'abd'];
let reg = new RegExp(keyword, 'i');
return list.filter(item => reg.test(item))
}