正则表达式( Regular Expressions)
介绍
什么是正则表达式?
由以下两种字符组成的文字模式
1、普通字符(例如26个英文字母、数字等)
2、特殊字符(有特殊含义的,例如.\等)
该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符串模式与所搜索的字符串进行匹配。
为什么使用正则表达式?
查找、替换、数据有效性验证
初始正则表达式
正则表达式的两种创建方式
字面量创建方式
var reg = /js/
构造函数创建方式
var reg = new RegExp('js')
两种创建正则表达式的区别?
当正则匹配的规则是个变量时,只能用构造函数创建方式,如:
var str = 'I love Js'
var userInput = 'love' //模拟用户输入
var pattern = new RegExp(userInput,'i')
console.log(parttern.test(str)) //true
console.log(parttern.exec(str)) //['love']
简单的小例子
var str = 'I love js'
var pattern1 = /js/
console.log(pattern1.test(str)) //true
console.log(pattern1.exec(str)) //['js']
var pattern2 = /Js/
console.log(pattern2.test(str)) //false
console.log(pattern2.exec(str)) //null
test:匹配到返回true,否则返回false
exec:匹配到返回数组,否则返回null
正则匹配是区分大小写的,怎样忽略大小写呢?我们接着学习,便知
正则表达式的模式修饰符
i ignoreCase 忽略大小写
g global 全局匹配
m multiline 多行匹配
var str = 'I love Js'
var pattern1 = /js/i
console.log(pattern1.test(str)) //true
console.log(pattern1.exec(str)) //['Js']
我们试着用另外的一种正则创建方式来做上题
var str = 'I love Js'
var pattern = new RegExp('js','i')
console.log(pattern.test(str)) //true
console.log(pattern.exec(str)) //['Js']
后面的两个模式修饰符,后面慢慢介绍。
简单的转义字符
转义字符: \
转义:将一种字符从一种含义转换为另一种含义
\n 换行
\t 制表符
\x0A 等同于 \n
\x61 就是a字符
\u0009 unicode编码 可以匹配中文字符
在正则的构造函数创建方式中,传入的是字符串,转义字符的数量是字面量方式的**双倍,**如下:
var pattern = /\b/
var pattern = new RegExp('\\b')
var pattern = /\\/
var pattern = new RegExp('\\\\')
字符类
[ abcd ] 匹配里面的任意一个字符,若水三千,只取一瓢,只要有一个匹配到,就是正确的。
var str = 'javascript'
var pattern = /[bcd]/
var result = pattern.exec(str)
console.log(result) //['c']
[ ^abcd ] 匹配除了a / b / c / d 以外的任意一个字符
var str = 'javascript'
var pattern = /[^abcd]/
var result = pattern.exec(str)
console.log(result) //['j']
[a-z] 匹配a到z字符的任意一个字符
[a-zA-Z] 匹配英文字符
[0-9] 匹配数字字符
常用的字符
. 除了换行符以外的任意字符
var str = '3.1415926'
var pattern = /\./ //匹配小数点
var pattern = /./ //匹配除了换行符以外的字符
console.log(pattern.exec(str)) // 3
[a-zA-Z0-9_] 等同于 \w
[^a-zA-Z0-9_] 等同于 \W
[0-9] 等同于 \d
[^0-9] 等同于 \D
\s 既能匹配空格又能匹配制表符,等一些uniode空格类的字符
\S 匹配除了空格、制表符和unicode空格类的字符
扩展: /\d\s/ 匹配数字空格,如‘9 ’ /[\d\s]/ 任意一个数字或者空格
量词
指定匹配的次数,如
var str = '110'
var pattern = /\d{3}/ //匹配3次 \d
console.log(pattern.exec(str))
指定匹配的次数范围
var str = '110'
var pattern_1 = /\d{1,2}/
var pattern_2 = /\d{1,}/ //至少匹配一次 等同于 /\d+/
var pattern_3 = /\d{0,}/ //至少匹配一次 等同于 /\d*/
var pattern_4 = /\d?/ // 等同于/\d{0,1}/ ==> ? === {0,1}
console.log(pattern_1.exec(str)) // 11
console.log(pattern_2.exec(str)) // 110
console.log(pattern_3.exec(str)) // 110
console.log(pattern_4.exec(str)) // 1
贪婪匹配:正则在默认的情况下,是贪婪的匹配,比如上面的pattern_1模式,只要情况允许,尽可能的多的进行匹配,贪婪匹配只存在于量词中
我们如何将贪婪匹配,转换为非贪婪匹配呢?
在正则后面加上一个问号,如
var str = 'aaab'
var pattern_1 = /a+/
var pattern_2 = /a+?/
console.log(pattern_1.exec(str)) // aaa 贪婪匹配
console.log(pattern_2.exec(str)) // a 非贪婪匹配
注意以下代码
v.ar str = 'aaab'
var pattern = /a+?b/
console.log(pattern.exec(str)) // aaab
问号只是尽可能的匹配
再来看一组案例,匹配<td><p>a</p></td> ?
var str = '<td><p>a</p></td><td><p>b</p></td>'
var pattern_1 = /<td>.*<\/td>/
var pattern_2 = /<td>.*?<\/td>/
console.log(pattern_1.exec(str)) //<td><p>a</p></td><td><p>b</p></td>
console.log(pattern_2.exec(str)) //<td><p>a</p></td>
如果不是非贪婪匹配,答案是不对的。
选择、分组和引用
选择
var str = 'css html js'
var pattern_1 = /html|css|js/
var pattern_2 = /c|css/
console.log(pattern_1.exec(str)) // css
console.log(pattern_2.exec(str)) // c
选择匹配是从左向右匹配,一旦匹配上了就忽略后面的匹配项
分组和引用
匹配多个ab?
var str = 'abab'
var pattern = /(ab)+/
console.log(pattern.exec(str)) //['abab','ab']
()内的表示分成一个组,组里的内容是一个整体,在匹配的时候,括号会有捕获性,即对括号里再进行匹配一次。
那么如何取消捕获呢?
var str = 'abab'
var pattern = /(?:ab)+/ //在前面加个 ?: 就可以取消括号的捕获性
console.log(pattern.exec(str)) //['abab']
多个分组的情况——平行的分组
var str = 'abcd'
var pattern = /(ab)(c)/
console.log(pattern.exec(str)) //['abc','ab','c']
多个分组的情况——嵌套的分组
var str = 'abcd'
var pattern = /(a(b(c)))/
console.log(pattern.exec(str)) //['abc','abc','bc','c']
分析:我们先把整体的括号看成没有,匹配整个模式,然后从左往右数,第一个括号为第一分组,第二个括号为第二个分组,第三个括号为第三个分组。分组要进行捕获性的匹配。
分组
var str = 'ab cd ab'
var pattern = /(ab) cd \1/ // \1代表第一个分组
console.log(pattern.exec(str)) //['ab cd ab','ab']
以上分组在实际中的应用,如下代码:
var str = '<text><a>这是一段文字</a></text>'
var pattern = /<([a-zA-Z]+)>(.*?)<\/\1>/
console.log(pattern.exec(str)) //['<text><a>这是一段文字</a></text>','text','<a>这是一段文字</a>']
扩展:exec函数,我们很早就接触了,但是在没有分组的情况下,只有一个数值。有分组后,就有多个数值。在这个数组中还有两个属性,index和input。
index是匹配的索引值,input是匹配的内容。注意不是捕获的。
位置匹配
首尾匹配
var str = 'js'
var pattern = /^js/ // ^匹配以js开头的
console.log(pattern.exec(str))
注意不要和/[^0-9]/里的搞混了
var str = 'html js'
var pattern = /js$/ // $ 只能匹配以js结尾的
console.log(pattern.exec(str))
单词边界匹配
var str = 'hello world nihao'
var pattern = /\bhello\b/
console.log(pattern.exec(str))
@、^、&等都是单词的边界
前瞻性匹配
var str = 'javascript'
var pattern = /java(?=script)/ //只有java后面跟的是script时,才匹配java
console.log(pattern.exec(str))
负向前瞻性匹配
var str = 'javascript'
var pattern = /java(?!script)/ //只有java后面跟的不是script时,才匹配java
console.log(pattern.exec(str))
RegExp对象的实例方法
我们在之前只是简单的使用exec、test方法,这里我们仔细讲一下。
exec方法
var str = 'js js js'
var pattern = /js/g
console.log(pattern.exec(str))//["js", index: 0, input: "js js js", groups: undefined]
console.log(pattern.exec(str))//["js", index: 3, input: "js js js", groups: undefined]
console.log(pattern.exec(str))//["js", index: 6, input: "js js js", groups: undefined]
console.log(pattern.exec(str))//null
console.log(pattern.exec(str))//["js", index: 0, input: "js js js", groups: undefined]
分析:index为匹配的是str中的第几个。当模式匹配为全局匹配时,每匹配一次,pattern.lastIndex会记录当前匹配完成后的后一个位置的索引。直到结束时,pattern.lastIndex的值会置为0
test方法
var str = 'js js js'
var pattern = /js/g
console.log(pattern.test(str))//true
console.log(pattern.test(str))//true
console.log(pattern.test(str))//true
console.log(pattern.test(str))//false
console.log(pattern.test(str))//true
test方法其实exec方法类似,只不过返回的是布尔值,分析如exec方法
其余方法
var pattern = new RegExp('a\\nb')
console.log(pattern.toString()) // '/a\nb/'
console.log(pattern.toLocaleString()) // '/a\nb/' 转换为具有本地特色的字符串
console.log(pattern.valueOf() === pattern) // true
这些方法都是继承过来的,是Object函数的原型对象里的方法。
RegExp对象的实例属性和构造函数属性
1、实例属性
var str = 'js js js'
var pattern = /js/ig
console.log(pattern.ignoreCase) //true 判断pattern后面是否带 i
console.log(pattern.global) //true 判断pattern后面是否带 g
console.log(pattern.multiline) //false 判断pattern后面是否带 m
console.log(pattern.source) // 和toString类似,都是返回以字面面量类似的字符串
console.log(pattern.lastIndex) //上面已经进行了介绍,这里就不过多的介绍了
2、构造函数属性
var str = 'js js js'
var pattern = /(j)s/
pattern.exec(str)
console.log(RegExp.input) // 'js js js' ,输出待匹配的字符串
console.log(RegExp.$_) //RegExp.input的别名
console.log(RegExp['$_'])
console.log(RexExp.lastMatch) //js 输出最近的一次匹配到的值
console.log(RegExp['$&']) //RegExp.lastMatch的别名
console.log(RegExp.leftContext) // '' ,输出匹配后,剩余的左边的字符串
console.log(RegExp['$`'])//RegExp.leftContext的别名
console.log(RegExp.rightContenxt) //' js js',输出匹配后,剩余的右边的字符串
console.log(RegExp["$'"])//RegExp.rightContext的别名
console.log(RegExp.lastParen)// 'j' ,输出最近的一次分组中捕获的内容
console.log(RegExp["$+"])//RegExp.lastParen的别名
console.log(RegExp.$1) //'j' 输出第一个分组中内容
String对象中与正则表达式相关的方法
search方法
var str = 'html js js'
var pattern = /js/g
console.log(str.search(pattern))//-5,返回第一次匹配到的素引,与g无关
//我们还可以直接在search中传入字符串,当然,这是将这里的字符串通过构造函数自动转化为正则的
console.log(str.search('js')) // -5
match方法
var str = 'js js js'
var pattern = /(j)s/
console.log(str.match(pattern)) // ['js','j']
var str = 'js js js'
var pattern = /(j)s/g
console.log(str.match(pattern)) // ['js','js','js']
match方法和exec方法有什么区别?
match:非全局的情况下才会返回分组中匹配到的内容,全局匹配只能匹配到所有匹配到的字符。
exec方法:无论是否全局匹配都会返回分组中匹配到的内容,都只会返回当前匹配到的一个内容,而不是全部返回。
这里介绍一下模式修饰符 m 的作用?
多行匹配 m 一般和尾修饰符,前修饰符,全局匹配一块用,如下代码所示
var str = '1.js\n2.js\n3.js'
var pattern = /js$/mg
console.log(str.match(pattern)) //['js','js','js'],每一行匹配到的js
var pattern = /js$/g
console.log(str.match(pattern) //['js'],只能匹配到一个,换行符没有什么用
split方法
不用正则时的用法
var str = 'html,css,js'
console.log(str.split(',')) //['html','css','js']
用正则时的用法
var str = 'html,css,js'
var patterm = /,/g //加不加g是无所谓的,split默认全局匹配
console.log(str.split(pattern)) //['html','css','js']
这样,用正则和不用正则不就没什么区别了吗!!!肯定不是这样的,如下所示
var str = 'html , css , js'
var patterm = /,/
console.log(str.split(pattern)) //['html ',' css',' js']
待匹配的字符串中,里面有不等的空格,我们用不用正则的方法显然是不能匹配出将空格去掉的字符串数组。这里我们可以用正则的方法,如下代码所示:
var str = 'html , css , js'
var patterm = /\s*,\s*/
console.log(str.split(pattern)) //['html','css','js']
replace方法
可以直接传字符串作为参数
var str = 'I love js js'
console.log(str.replace('js','html'))// 'I love html js'
字符串传参,只能将第一个匹配到的改变值。不能进行全局替换。我们用正则的方式,可以进行全局替换,如下代码所示:
var str = 'I love js js'
var pattern = /js/g
console.log(str.replace(pattern,'html'))// 'I love html html'
replace正则的第一个用法
var str = 'I love js'
var pattern = /(js)/
document.write(str.replace(pattern,'<strong style="color:red">$1</strong>'))
replace正则的第二个用法:过滤敏感词
var str = '你好匹配不好匹配匹配太好匹配匹配你好不好'
//这里的的你好、不好、太好是敏感词汇
var pattern = /你好|不好|太好/g
console.log(str.replace(pattern,function($0,$1){//$0表示匹配到的内容,$1表示第一个分组匹配到的内容,这里没有分组
var result = ''
for(var i = 0;i < $0.length;i++){
result +='*'
}
return result
}))
常用的正则验证
验证用户输入的是不是QQ号?
QQ号的规则:1.全是数字 、2.首位不能为0、3.最少5位(10000)、4.目前最多11位
var pattern = /^[1-9]\d{4,}$/
验证用户昵称?
昵称规则:1.2-18位 、2.中英文、数字及下划线
var pattern = /^[\u4e00-\u9fa5a-zA-Z0-9_]{2,18}$/
var pattern = /^[\u4e00-\u9fa5\w]{2,18}$/
验证密码是否合法?
密码规则:1.6-18位 、 2.不能用空白字符 、 3.区分大小写
var pattern = /^\S{6,16}$/
var pattern = /^[\w~!@#$%^\]{6,16}$/ //穷举法
去除字符串首位的空白字符?
var str = ' Alex '
var pattern = /^\s+|\s+$/g //第一种方式
console.log('|'+str.replace(pattern,'')+'|')
var str = ' Alex '
var pattern1 = /^\s+/ //第二种方式
var pattern2 = /\s+$/
console.log('|'+str.replace(pattern1,'').replace(pattern2,'')+'|')
封装成一个方法
function trim(str){
return str.replace(/^s+/,'').replace(/\s+$/,'')
}
转驼峰
var str = 'background-color'
var pattern = /-([a-z])/gi
console.log(str.replace(pattern,function(all,letter){
return letter.toUpperCase()
})) //'backgroundColor'
封装成一个函数
function toCamelCase(str){
return str.replace(pattern,function(all,letter){
return letter.toUpperCase()
})
}
匹配HTML标签
var str = '<p class="odd" id="odd">123</p>'
var pattern = /<\/?[a-zA-Z]+(\s+[a-zA-Z]+=".*")*>/g
console.log(pattern.match(pattern))
逆向思考一下
var str = '<p class="odd" id="odd">123</p>'
var pattern = /<[^>]+>/g
console.log(pattern.match(pattern))
上述的可能会存在一些问题,比如如果双引号里有 > 这个符号,就会出现匹配错误的现象,我们进行完善,代码如下:
var str = '<input type="text" value=">" name="username" />';
var pattern = /<(?:[^"'>]|"[^"]*"|'[^']*')*>/g;
console.log(str.match(pattern));
var str = '<input type="text" value=">" name="username" />';
var pattern = /<(?:[^"'>]|(["'])[^"']*\1)*>/g;
console.log(str.match(pattern));
邮箱验证
例如:fang.123456.com
第一种方式
var pattern = /(?:\w+\.)*\w+@(?:\w+\.)+[a-z]/i
第二种方式
var pattern = /^[a-z0-9]+(?:[._-][a-z0-9]+)*@[a-z0-9]+(?:[._-][a-z0-9]+)*\.[a-z]{2,4}$/i