什么是正则表达式
Regular Expression 使用单个字符串来描述、匹配一系列符合某个句法规则
的字符串
按照某一种规则
去匹配符合条件的字符串
修饰符
g
名称:global
说明:全文搜索,不添加的话,搜索到第一个匹配停止
i
名称:ignore case
说明:忽略大小写,默认大小写敏感
m
名称:multiple linse
说明:执行多行匹配
元字符
- 正则表达式由两种基本字符类形成:
原义文本字符
元字符 - 元字符是在正则表达式中有特殊含义的非字母字符
* + ? $ ^ . | / : = ( ) { } [ ]
字符 | 含义 |
---|---|
水平制表符 | |
v | 垂直制表符 |
换行符 | |
回车符 | |
o | 空字符 |
f | 换页符 |
cX | 与X对应的控制字符(Ctrl+X) |
xnn | 由十六进制数nn指定的拉丁字符,列如:x0A等价于 |
uxxxx | 由十六进制数xxxx指定的Unicode 字符,列如u0009等价于 |
字符类
- 我们可以使用元字符
[]
来构建一个简单的类 - 所谓类是指符合某些特性对象,一个泛指,而不是特指某个字符
- 表达式
[abc]
把字符a
或b
或c
归为一类,表达式可以匹配这类的字符
var reg = /[abc]/g
'1a6c1d6v5d1b5'.replace(reg, 'X')
// "1X6X1d6v5d1X5"
// 我们将当前字符串中的abc 替换成了X
字符类取反
- 使用元字符 ^ 创建反向类/负向类
- 反向类的意思是不属于某类的内容
- 表达式
[^abc]
表示,不是字符a 或 b 或 c 的内容
var reg = /[^abc]/g
'1a6c1d6v5d1b5'.replace(reg, 'X')
// "XaXcXXXXXXXbX"
// 我们将当前字符串中不是abc的所有内容 替换成了X
范围类
- 我们可以使用
[a-z]
来连接两个字符表达式从a到z的任意字符,这是一个闭区间,也就包含a和z本身。
var reg = /[a-z]/g
'a1b2c3d45z3x'.replace(reg, 'Y')
// "Y1Y2Y3Y45Y3Y"
// a-z的所有字符替换为Y
// 范围类也可以连这写
var reg = /[a-zA-Z]/g
'QDa1B2c3d45Z3xASDK'.replace(reg, 'Y')
// "YYY1Y2Y3Y45Y3YYYYY"
// a-z和A-Z的所有字符替换为Y
// 如果我们在范围类中,比如说需要匹配这样的 8375-32*29 将-以及小于我的全部替换成Y
var reg = /[1-5-]/g
'8375-32*29'.replace(reg, 'Y')
// "8Y7YYYY*Y9"
字符类
字符 | 等价类 | 含义 |
---|---|---|
. | [^ ] | 除了回车和换行符之外的所有字符 |
d | [0-9] | 查找数字 |
D | [^0-9] | 非数字字符 |
s | [ x0Bf ] | 空白符 |
S | [^ x0Bf ] | 非空白符 |
w | [a-zA-Z_0-9] | 字母 数字 下划线 |
W | [^a-zA-Z_0-9] | 非单词字符 |
[...] | 方括号内的任意字符 | |
[^...] | 不在方括号内的任意字符 | |
[] | 退格直接量(特列) |
边界
字符 | 含义 |
---|---|
^ | 以xxx开始 |
$ | 以xxx结尾 |
单词边界 | |
B | 非单词边界 |
(?=p) | 零宽正向先断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符 |
(?!p) | 零宽负正先行断言,要求接下来的字符不与p匹配 |
量词
量词 | 含义 |
---|---|
{n} | 匹配前一项n次 |
{n,m} | 匹配前一项至少n次,但不能超过m次 |
{n,} | 匹配前一项n次或者更多次 |
+ | 出现一次或多次 (最少出现一次)等价于{1,} |
* | 出现零次或多次 (任意次)等价于{0,} |
? | 出现零次或一次(最多出现一次) |
贪婪模式
var reg = /d{3,6}/g
'12345678'.replace(reg, 'X')
// X78
// 他匹配了123456 而78没有被匹配
// 我们这个正则表达式 是匹配3-6次,可以满足3 4 5 6 都可以满足,而这时候正则表达会尽可能多的匹配,直到匹配失败
//
非贪婪模式
让正则表达式尽可能少的匹配,一旦成功匹配不再继续尝试就是非贪婪模式。
做法很简单,只要再量词后面加上?
即可
var reg = /d{3,6}?/g
'12345678'.replace(reg, 'X')
// XX78
// 他匹配了 123 456
// 3 次一组,后面两个没有被匹配掉
分组
使用()可以达到分组的功能,使量词作用与分组
var reg = /([a-z]d){3}/g
'a1b2c3d4'.replace(reg, 'X')
// Xd4
// 在使用或的时候,我们同样需要分组
var reg = /(java|type)script/g
'javascripttypescript'.replace(reg, 'X')
// XX
反向引用
var reg = /(d{2})-(d{2})-(d{4})/g
'02-13-2020'.replace(reg, '$3/$1/$2')
// 2020/02/13
// 他会捕获每一个分组,分别从$1-$n
忽略分组
不希望捕获某些分组,我们只需要再分组内加上?:
就可以
var reg = /(?:d{2})-(d{2})-(d{4})/g
'02-13-2020'.replace(reg, '$3/$1/$2')
// $3/13/2020
// 我们可以看到我们只有$1 和 $2 捕获到了
前瞻
- 正则表达式从文本头部向尾部开始解析,文本尾部方向,称为:
前
文本尾部叫做后
。 前瞻
就是在正则表达式匹配到的规则的时候,向前检查是否符合断言(断言就是前瞻语法的一部分),后顾/后瞻方向相反。- 前瞻比如:当我们匹配到一个人叫张三的时候,我们还需要往前看看他爸爸是否叫张二
- JavaScript 不支持后顾
- 符合和不符合特定断言称为
肯定/正向
匹配和否定/负向
匹配
名称 | 正则 | 含义 |
---|---|---|
正向前瞻 | exp(?=assert) | |
负向前瞻 | exp(?!assert) | |
正向后顾 | exp(?<=assert) | JavaScript 不支持 |
负向后顾 | exp(?<!assert) | JavaScript 不支持 |
// 正向前瞻
// 匹配单词字符,并且要求后面需要是数字
var reg = /w(?=d)/g
'a2*3'.replace(reg, 'X')
// X2*3
'a2*3dasd2e5'.replace(reg, 'X')
// X2*3dasX2X5
// 负向前瞻
var reg = /w(?!d)/g
'a2*3'.replace(reg, 'X')
// aX*X
'a2*3dasd2e5'.replace(reg, 'X')
// "aX*XXXXdXeX"
Js 正则 对象属性
每一个RegExp对象都包含5个属性。
- global:是否全文搜索, 默认
false
- ignore case:是否大小写敏感,默认
false
- multiline: 多行搜索,默认值是
false
- lastIndex: 当前匹配结果的 最后一个字符的 下一个字符
- source:source是一个只读的字符串,包含正则表达式的文本。
var reg1 = /w/
var reg2 = /w/gim
reg1.global false
reg1.ignoreCase false
reg1.multiline false
reg1.global = true 这是不起作用的
reg1.global false
reg2.global true
reg2.ingnoreCase true
reg2.multiline true
reg1.source "w"
reg2.source "w"
reg1.lastIndex 0
reg2.source 0
Js 正则 方法
test
- 用于测试字符串参数中是否存在匹配正则表达式模式的字符串
- 如果存在则返回
true
,否则返回false
var reg1 = /w/
var reg2 = /w/g
reg1.test('a') true
reg1.test('|') false
reg1.test(')') false
// 判断当前字符串 是否包含传递的值
当我们加上g的时候 我们多执行几次结果,会反向奇怪的现象
我们来开一个有意思的例子:
reg2.test('ab') true
console.log(reg2.$1)
reg2.test('ab') true
reg2.test('ab') false
reg2.test('a') true
reg2.test('a') false
/*
原因:这个其实是lastIndex在作怪
reg2.test('ab') 拆分
第一遍执行 我们找当前匹配结果 a,a所在字符的位置是1,所以lastIndex = 1
第二遍执行 当前匹配结果的最后一个字符 的下一个字符 b,b所在字符位置是2,所以lastIndex = 2
第三遍执行的时候 后面没有可以匹配到的字符了 所以返回为false 并且将 lastIndex1 重置为 0
所以第四次匹配的时候,他有开始重新查找 返回为true
注意: 每次匹配的时候,他把上一个结果记住了,从下一个开始
*/
解决方法:
// 每次都实列化一个新的
(/w/g).test('a')
exec
- 使用正则表达式模式对字符串执行搜索,并将更新全局RegExp对象的属性以反映匹配结果。
- 如果没有匹配到文本返回null,否则返回一个结果数组:
- index 表示匹配结果第一个字符的位置
- input 存放被检索的字符串
string
非全局调用
- 调用非全局的RegExp对象的exec() 时,返回数组
- 第一个元素是与正则表达式相匹配的文本
- 第二个元素是与RegExpObject 的第一个子表达式相匹配的文本(如果有的话)
- 第三个元素与RegExp 对象的第二个子表达式相匹配的文本(如果有的话),以些类推
var reg1 = /d(w)d/;
var reg2 = /d(w)d/g;
var str = '1a2b3c4d5e'
var ret = reg1.exec(str)
console.log(`${reg1.lastIndex} ${ret.index} ${ret}`)
// lastIndex在全局里面才显示
// index 从第几个字符开始匹配
// [匹配结果, 分组]
// 0 0 ["1a2", "a"]
console.log(`${reg1.lastIndex} ${ret.index} ${ret}`)
// 0 0 1a2,a
while(ret = reg2.exec(str)) {
console.log(`${reg2.lastIndex} ${ret.index} ${ret.toString()}`)
// 3 0 1a2,a
// 7 4 3c4,c
/** lastIndex
第一次匹配,1a2 lastIndex 索引记录到 3
第二次匹配,3c4 lastIndex 索引记录到 7
*/
/** index
第一次匹配,当前匹配到的,第0向开始
第二次匹配,当前匹配到的,第4向开始
*/
}
正则表达式的字符串方法
search
- search 方法用于检索字符串中指定的字符串,或检索与正则表达式相匹配的子字符串
- 方法返回第一个匹配结果 index,查不到返回
-1
- search 方法不执行全局匹配,它将忽略标志g,并且总是从字符串的开头进行检索
'a1b2c3d4e1'.search('1') 1
'a1b2c3d4e1'.search('10') -1
'a1b2c3d4e1'.search(1) 1
// 为什么 写数字1 也可以呢
// 原因:我们传递的参数不是正则的时候,他就尝试将我们传递的参数转换为正则。
'a1b2c3d4e1'.search(/1/) 1
'a1b2c3d4e1'.search(/1/g) 1
// 开启全局模式和不开启全局模式,结果都一样
match
- match 方法将检索字符串,以找到一个或多个与regexp匹配的文本
- regexp 是否具有标志g 对结果影响很大
非全局调用
- 如果regexp没有标志g,那么match 方法就只能在字符串中执行一次匹配
- 如果没有找到任何匹配的文本,将返回null
- 否则它将返回一个数组,其中存放了与找到的文本匹配文本有关的信息
- 返回数组的第一个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本
- 除了常规的数组元素之外,返回的数组还包含有 2个对象属性
- index 生命匹配文本的起始字符在字符串的位置
- input 生明对stringObject的引用
var reg1 = /d(w)d/
var str = 'a1b2c3d4e5'
var ret = str.match(reg1)
ret ["1b2", "b"]
ret.index 1
reg1.lastIndex 0
全局调用
- 如果 regexp 具有标志 g 则 match 方法将执行全局检索,找到字符串中的所有匹配字符串
- 没有找到任何匹配的字符串,则返回null
- 如果找到了一个或多个匹配字符串,则返回一个数组
- 数组元素中存放的是字符串中所有的匹配字符串,而且也没有index属性或input 属性
var reg2 = /d(w)d/g
var str = 'a1b2c3d4e5'
var ret = str.match(reg2)
ret ["1b2", "3d4"]
ret.index undefined
reg2.lastIndex 0
splic
- 我们经常使用 split 方法把字符串分割为字符数组
'a,b,c,d'.split(','); // ['a', 'b', 'c', 'd']
- 在一些复杂的分割情况下,我们还可以使用正则表达式解决
'a1b2c3d'.split(/d/); // ["a", "b", "c", "d"]
说明: split 其实默认也是给我们尝试转成正则表达式 /,/
replace
- 参数1 string | reg 找谁
- 参数2 string | function 替换成谁
- function 会在每次匹配替换的时候调用,有四个参数 (注意:参数不固定)
- 匹配字符串
- 正则表达式分组内容,没有分组则没有该参数
- 匹配项在字符串中的index
- 原字符串
- function 会在每次匹配替换的时候调用,有四个参数 (注意:参数不固定)
'a1cde'.replace('1', 'b') // abcde
'a1cde'.replace(/d/, 'b') // abcde
'a1b2c3d4'.replace(/d/g, (match, index, origin)=>{
return parseInt(match) + 1
}) // a2b3c4d5
'a1b2c3d4'.replace(/(d)(w)(d)/g, (match, group1, group2, group3, index, origin)=>{
console.log(group1, group2)
return group1 + group3
}) // a12c34