正则表达式
定义
又称规则表达式 ———— 则通常被用来检索、替换那些符合某个模式(规则)的文本。
正则只能作用在字符串上
基础学习
定义规则表达式
-
字面量形式
/规则表达式/[修饰符]
规则可以是普通的字符串 斜杠中直接放入字符串,不需要加引号
let re = /abc/ console.log(re)// /abc/
-
利用构造函数
new RegExp(规则(字符串),[修饰符])
let re2 = new RegExp('abc') console.log(re2)// /abc/
-
区别:
当规则存在变量中时,需要使用构造函数的形式
let s = 'peace' let re2 = new RegExp(s) console.log(re2)// /peace/
转义字符
转义符 \
let str = 'miaov\'ketang';
console.log(str);// miaov'ketang
'—-一个单引号 \d —–代表数字 " —–一个单引号
或
或的意思
[ ] [1-40]匹配的是1,2,3,4,0并不是1~40
let str = 'h1hj33jhjgh3j2323hjh12k'
let re = /[3kh]/g;
let re2 = /[1-5]+/g;//表示匹配1~5
console.log(str.match(/3|k|h/g))
console.log(str.match(re))
console.log(str.match(re2))//["1", "33", "3", "2323", "12"]
修饰符
-
g — 全局匹配
/\d/g --- 全局搜索数字,从头到尾搜索,中间不停止
-
i — 忽略大小写
/m/i --- M和m都能找到
元字符
-
\d — 数字 , 等价于 [0-9]。
/\d/ --- 从头到尾搜索,有一个满足就停止
- \D — 匹配一个非数字字符。等价于 [^0-9]。
- \f — 匹配一个换页符。等价于 \x0c 和 \cL。
- \n — 匹配一个换行符。等价于 \x0a 和 \cJ。
- \r — 匹配一个回车符。等价于 \x0d 和 \cM。
- \s — 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
- \S — 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
- \t — 匹配一个制表符。等价于 \x09 和 \cI。
- \v — 匹配一个垂直制表符。等价于 \x0b 和 \cK。
- \w — 匹配字母或数字或下划线或汉字。等价于’[A-Za-z0-9_]’。
- \W — 匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]‘。
- ? — 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,’o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。
- . — 匹配除 “\n” 之外的任何单个字符
-
x y — 匹配 x 或 y。例如,’z food’ 能匹配 “z” 或 “food”。’(z f)ood’ 则匹配 “zood” 或 “food”。 - [xyz] — 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。
- [^xyz] — 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’、’l’、’i’、’n’。
- [a-z] — 字符范围。匹配指定范围内的任意字符。例如,’[a-z]’ 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符
- [^a-z] — 负值字符范围。匹配任何不在指定范围内的任意字符。例如,’[^a-z]’ 可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。
限定符
- 有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种
* — 可以不出现,最多不限 {0,}
- — 至少出现一次 {1,} ? — {0.1} {n,m} — n < m, n:最少出现次数 m:最多出现次数
定位符
定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。
定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
^ —– 匹配输入字符串开始的位置 $ —– 匹配输入字符串结尾的位置 \b —- 匹配单词的开始或结束,匹配一个字边界,即字与空格间的位置,就是
\w
的左边或右边如果不是\w
,就是一个边界。书写位置很重要abcdefg
/\babc/
往后匹配,/efg\b/
从边界往前匹配 \B —- 非字边界匹配
let str = 'w1tw23tw456tw7890tw12345twt';
let re = /\d/;
let re2 = /w\dt/g;//一次
let re3 = /w\d+t/g;//最少一次
let re4 = /w\d*t/g;//不限
let re5 = /w\d?t/g;//最多一次
console.log( str.match(re) )//["1", index: 1, input: "w1tw23tw456tw7890tw12345twt"]
console.log( str.match(re2) )//["w1t", "w23t", "w456t", "w7890t", "w12345t"]
console.log( str.match(re3) )//["w1t", "w23t", "w456t", "w7890t", "w12345t"]
console.log( str.match(re4) )//["w1t", "w23t", "w456t", "w7890t", "w12345t", "wt"]
console.log( str.match(re5) )//["a1f", "a7f", "a2f", "af"]
正则方法
test(str)
作用:用来检测指定的字符串是否符合正则的规则 语法:regExObj.test(string) 参数:被检索的字符串 返回值:true或false
-
regExObj.test(str)
regExObj代表定义的正则 str代表被检索字符串
let re = /123/
//定义的正则规则
text.oninput = function(){
if(re.test(this.value)){
warn.innerHTML = '有123'
}else{
warn.innerHTML = '没123'
}
}
test()记录上一次执行位置
let str = '123456789'
let re = /\d{5,5}/g
console.log(re.test(str))//true
console.log(re.lastIndex)//5
console.log(re.test(str))//false
console.log(re.lastIndex)//0
//-----------解决办法---------------------
console.log(re.test(str))//true
re.lastIndex = 0;
console.log(re.test(str))//true
以上代码第一次执行的时候,匹配的是12345,test会记录该次执行的位置5
第二次执行的时候,test会从第5位开始查找,但是最少要5位,6789只有4位,不满足,返回false
match()
作用:检索指定字符串或正则表达式匹配到的字符串 语法:string.match( 正则|指定字符 ) 参数:字符串或正则表达式 返回值:有匹配到的返回到一个数组中,没有返回null
-
返回一个值时,带其他属性,包括
1. 正则中有分组信息,()没有全局匹配g,捕获分组信息;有g,不捕获 2. index:匹配到的字符的位置 3. input:被查找的完整字符串
let str = 'wo1r23kd45Pe67ac890e';
let re = /\d/;
console.log( str.match(re) )
//["1", index: 2, input: "wo1r23kd45Pe67ac890e"]
- 返回多个值时,不带其他属性
let str = 'wo1r23kd45Pe67ac890e';
let re2 = /\d/g;
let re3 = /\d+/g;
console.log( str.match(re2) )
//["1", "2", "3", "4", "5", "6", "7", "8", "9" , "0"]
console.log( str.match(re3) )
//["1", "23", "45", "67", "890"]
search()
string.search( string re );
作用:检索指定字符串或正则表达式匹配到的字符串首次出现的位置 语法:string.search( 正则|指定字符 ) 参数:字符串或正则表达式 返回值:有匹配到的返回指定字符或正则出现的位置 number
作用类似于字符串方法的indexOf,但是更强大,参数除了可以是指定字符外,还可以是正则表达式
let str = 'w1tw23tw456tw7890tw12345twt';
console.log( str.indexOf('0') )//16
let re = /0/
console.log( str.search('0') )//16----可以是字符串
console.log( str.search(re) )//16----也可以是正则
replace()
用来替换字符串中的某些符合规则的字符 语法:string.replace(string|regexObj,string|function) 参数: 1. 第一个参数:字符串或正则表达式 2. 第二个参数:字符串或函数 返回值:
//将匹配字符替换为str-----第二个参数为字符串
string.replace(string|regexObj,str)
//将匹配字符替换为str-----第二个参数为函数
string.replace(string|regexObj,function($0,$1,$2){
//$0:满足规则的每个字符
//$1:满足规则的每个字符所在的位置
//$2:被检索的完整字符串
return '*'//将满足规则的字符替换为*,return后的字符就是要替换的字符
})
- 正则中没有()分组时:
函数参数1:满足规则的每个字符 函数参数2:满足规则的每个字符所在的位置 函数参数3:被检索的完整字符串
- 正则中有一个()分组时:
函数参数1:满足规则的每个字符 函数参数2:满足分组正则的字符 函数参数3:满足规则的每个字符所在的位置 函数参数4:被检索的完整字符串
let str = '1asd2'
let re = /(\d)\D+(\d)/g;
let str2 = str.replace(re,function(...data){
console.log(data)
//["1asd2", "1", "2", 0, "1asd2"]
// $1, $2, $3, $4, $5
})
- 正则中有多个()分组时:
函数参数1:满足整体规则的每个字符 函数参数2:参数1中满足分组正则1的字符 函数参数3:参数1中满足分组正则2的字符 函数参数4:满足规则的参数1所在的位置 函数参数5:被检索的完整字符串
let str = 'a23ba456ba7b'
let re = /(\d)\D+(\d)/g;
let str2 = str.replace(re,function(...data){
console.log(data)
//["3ba4", "3", "4", 2, "a23ba456ba7b"]
//["6ba7", "6", "7", 7, "a23ba456ba7b"]
//
})
运算符优先级
- 由高到低排序
转义符
- \
圆括号和方括号
- (), (?:), (?=), []
限定符
- *, +, ?, {n}, {n,}, {n,m}
定位点和序列(即:位置和顺序)
- ^, $, \任何元字符、任何字符
替换
分枝条件
\d{5}-\d{4} \d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5} \d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。
分组()
捕获
() 的意思是表示中间是整体
有()默认捕获分组信息,
(?:/\d/g) 不捕获分组信息,也不分配组hao
let t1 = '2017---8-----25'
//-----------默认捕获---------------
let re = /(\d+)\D+\d+\D+\d+/g;
t1.replace(re,(...data)=>{
console.log(data)//["2017---8-----25", "2017", 0, "2017---8-----25"]
return data[1]+'年'+data[3]+'月'+data[5]+'日'
})
//------------设置不捕获-----------
let re1 = /(?:\d+)\D+\d+\D+\d+/g;
t1.replace(re1,(...data)=>{
console.log(data)//["2017---8-----25", 0, "2017---8-----25"]
return data[1]+'年'+data[3]+'月'+data[5]+'日'
})
//?: 设置不捕获,分组匹配到的字符不会记录
分组后向引用
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本
如:(\w+){2}a\1—-\1代表的就是括号(\w+)搜索到的字符,可以在后面复用。
-
自定义组名
(?<ab>\w+)
或(?'ab'\w+)
这样就可以把该组改名为ab了 -
引用自定义组名
\k<ab>
如:\b(?<ab>\w+)\b\s+\k<ab>\b
就可以引用了 -
组名的分配
分组0指的是整个表达式 第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
零宽断言
(?=exp) 匹配exp前面的位置
零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.
时,它会匹配sing和danc
(?<=exp) 匹配exp后面的位置
零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book
时,它匹配ading
- (?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)
(?!exp) 匹配后面跟的不是exp的位置
零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字
(?< !exp) 匹配前面不是exp的位置
零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字
- (?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容
- (?<=<(\w+)>) 指定了这样的前缀:被尖括号括起来的单词(比如可能是< b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是< b>的话,后缀就是< /b>了。整个表达式匹配的是< b>和< /b>之间的内容(再次提醒,不包括前缀和后缀本身)
贪婪模式和懒惰模式
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符
- *? 重复任意次,但尽可能少重复
- +? 重复1次或更多次,但尽可能少重复
- ?? 重复0次或1次,但尽可能少重复
- {n,m}? 重复n到m次,但尽可能少重复
- {n,}? 重复n次以上,但尽可能少重复
let t1 = 'abacbacob';
//找到a开头b结尾的
let re = /a.*b/g;//贪婪模式:尽最大可能找到最大范围的字符,从字符串尾部开始向前搜索
console.log( t1.match(re)) //["abacbacob"]
let re2 = /a.*?b/g//懒惰模式:从前往后搜索,挨个检索
console.log( t1.match(re2)) //["ab", "acb", "acob"]
//语法:在相应的正则规则后加?
自定义模板引擎
通过正则检索到对应的特殊字符,对里面的变量进行replace成定义数据的对应变量
<div id="box"></div>
//定义模板字符串
let html = `
<p id="op">@@title@@</p><p id="ap"><div class="hob">@@list1@@</div><div class="hob">@@list2@@</div><div class="hob">@@list3@@</div></p>
`
//对应数据
let obj = {
title:'谦哥的三大爱好:',
list1:'抽烟',
list2:'喝酒',
list3:'烫头'
}
//检索正则
let re = /@@(.+?)@@/g;
//此处要有?,执行懒惰模式,否则会匹配从@@开始到@@结束的整个字符串
//@@title@@</......ass="hob">@@list3@@
//检索后replace函数
function diy(){
return html.replace(re,($0,$1,$2)=>{
console.log($1)
return obj[$1]
})
}
box.innerHTML = diy();
/*
谦哥的三大爱好:
抽烟
喝酒
烫头
*/