共计 8921 个字符,预计需要花费 23 分钟才能阅读完成。
正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExp
的 exec
和 test
办法, 以及 String
的 match
、matchAll
、replace
、search
和 split
办法。
正则的用意
正则表达式是 匹配模式
,要么匹配 字符串
,要么匹配 地位
。
正则表达式是 匹配模式
,要么匹配 字符串
,要么匹配 地位
。
正则表达式是 匹配模式
,要么匹配 字符串
,要么匹配 地位
。
正则的创立
-
形式一:字面量的创立
const reg = //
-
形式二:构造函数
const reg = new RegExp()
补充:
字面量
是罕用的形式。-
字面量创立 和 构造函数的创立 是存在区别的
// eg: const num = 12.34 // 字面量 const reg = /\d+.\d+/ // 构造函数 const reg = new RegExp('\d+.\d+')
reg 的 正则字符串是一样的,然而它们代表一样的意思吗?不是
先看一段代码:
console.log('\') // 报错 Javascript: unterminated string literal console.log('\d') // d
在这里
\
,在 JS 解析的时候是有非凡意义的。所以下面的构造函数形式的创立:
const reg = new RegExp('\d+.\d+') // d+.d+ 匹配的 d 这个字符 // 须要本义 const reg = new RegExp('\d+\.\d+')
这种写法挺麻烦的,所以举荐应用 字面量的写法。
正则的原子
原子是正则表达式的最根本的组成单位,而且在每个模式中起码蕴含一个原子。
原子分为可见原子和不可见原子。
- 可见原子: Unicode 编码表中键盘输入肉眼可见的字符。
- 不可见原子: Unicode 编码表中键盘输入肉眼不可见的字符, 如空格,制表符。
通用字符类型:
\d 匹配 0 - 9 任意一个数字[0-9]
\D 除了数字以外的任意一个字符匹配[^0-9]
\w 数字 字母 下划线匹配[a-zA-Z0-9]
\W 除了数字 字母 下划线以外的任意一个字符[^a-zA-Z0-9]
\s 与任意一个空白字符匹配
\S 与任意一个空白字符意外的字符匹配[^\n\f\r\t\v]
\f 换页字符;
\n 换行字符;
\r 回车字符;
\t 制表符;
\v 垂直制表符;
正则的特殊字符
开始(^
)和 完结($
)
对匹配的做开始的限定和完结的限定
const reg = /abc/ // abc aabc abcabc 这些都是匹配胜利
const reg = /^abc$/ // 只能
转义字符(\
)
\
这个无论是在 正则还是在其余的中央都有着非凡的意义。
正则中有跟多的符号是有意义的,如果想要匹配他们都是须要本义的
\ () [] ^ $ * + ?
这些符号都是有着本人各自的意义,上面会顺次介绍
const str = '()[]^$*+?' // 这是一个字符串
const reg = /\(\)\[\]\^\$\*\+\?/ // 大量的应用本义
或(|
)
左右两边看成一个整体,满足其中一个就行
const reg = /abc|acc/ // 匹配 abc 或者 acc
点元字符(.
)
匹配除 \n
之外的任何单个字符。
let str = `
james
kobe
`
console.log(str.match(/.+/)) // 'james'
.
基本上都匹配了所有。那么匹配全副该怎么写呢?
// 匹配所有
const reg = /[\d\D]+/
const reg = /[\s\S]+/
const reg = /.+/s // s 模式
正则的量词(* + ?
)
{m,n}
: 匹配 m 到 n 个字符,都是数字,还能够写成{m,}
:m 到有限个
const reg = /d{2,4}/ // 匹配 d 的数量为 2 到 4 个
*
: 0 个到无穷个 等价于 {0,}
const reg = /d*/
+
: 1 个到无穷个 等价于 {1,}
const reg = /d+/
?
: 1 个或者 0 个
const reg = /d?/
留神: * + ? {m,n}
它们都是 贪心模式,上面会介绍。
原子表([]
)
[]
满足其中的一个内容
const reg = /[abc]/ // a b c ab 都满足
区间匹配
[0-9] // 匹配 0 - 9 的数字
[a-z] // 匹配 a - z 的小写字母
[A-Z] // 匹配 A - Z 的大写字母
取反
[^abc] // 匹配除了 abc 以外的字符
这里 ^
尽管跟开始字符是一样的,然而示意的意义不一样。这里的 ^
是在 []
应用。
不解析字符
[.()+] // 在原子表中,它们都是单纯的字符
原子组(()
)
()
与 原子表 []
不同,()
会把其中的内容看成一个整体。
const dom = `<h1>copyer</h1>`
const reg = /^<h([1-6])>[\s\S]*</h([1-6])>$/i
// 等价于
const reg1 = /^<h([1-6])>[\s\S]*</\1>$/
([1-6])
看成一个整体,就是为了匹配 h1~h6
的标签
在第一个正则中,匹配 开始标签 和完结标签,就会写两次的([1-6])
,看起来是比拟麻烦的
1、组名
组名:就是为了下面反复写的问题。
从左到右,顺次找出 原子组 ()
,排在第几个,就应用\num
就行了;
比方:([1-6])
排在原子组的第二个,那么应用 \2
就能够了。
简略记忆:只有判断了是原子组,就数 (
从左到右排在第几个
2、嵌套组
// 匹配 url
const url = 'http://www.baidu.com'
const reg = /^https?://[\w]+.[\w]+.(com|cn)$/
console.log(url.match(reg))
match 办法
前面也会总结
[
0:"http://www.baidu.com"
1: "com"
groups: undefined
index: 0
input: "http://www.baidu.com"
]
数组第一个,就是匹配到的字符串
如果有 原子组 的应用,匹配到的后果,就会顺次放在第一个的前面
3、不记录组
下面不是对com
组提取进去了嘛,如果不想要记录该怎么做呢?
const reg = /^https?://[\w]+.[\w]+.(?:com|cn)$/
在原子组中:应用?:
就不会记录在组中
(?:com|cn)
?:
就是放在最开始
4、组的理论应用场景
给一个 dom 节点字符串,拿取外面的内容
let str = '<h1>copyer</h1>'
const reg = /<h([1-6])>(.*)</h([1-6])>/
console.log(str.match(reg)[2])
下面应用了三个原子组:([1-6])
(.*)
([1-6])
这个三个都是 push 到数组中,内容就是第二个原子组,所以,咱们取下标为 2,就能够拿到内容了
正则的贪心模式
在下面,晓得了 正则的量词(* + ?
) 的根本应用,然而呢,它们还是有一种个性:贪心
何为贪心?
const str = 'aaaaaa'
const reg = /a+/
console.log(str.match(reg)) // aaaaaa
只有在条件的满足范畴内,能匹配到多少就是多少,贪心思维
那么怎么 阻止贪心 呢?
正则量词
前面增加一个?
const str = 'aaaaaa'
const reg = /a+?/
console.log(str.match(reg)) // a
*? +? ?? {m,n}?
相似这种组合
正则的属性lastIndex
lastIndex
从字面了解:最初一个索引值。实际上,它的意思是 正则表达式开始下一次查找的索引地位。
lastIndex
第一次的时候总是为 0 的,
第一次查找完了的时候会把 lastIndex
的值设为匹配到得字符串的最初一个字符的索引地位加 1
const str = 'aaa_aa_aaaa'
const reg_g = /a+/g
console.log(reg_g.lastIndex) // 0 默认值
console.log(reg_g.exec(str)) // aaa
console.log(reg_g.lastIndex) // 3 上次匹配 aaa 下标值为 2,在下面的一次加 1,所以为 3
console.log(reg_g.exec(str)) // 所以这次匹配是在 下标为 3 的地位开始匹配
如果没有匹配到的话,lastIndex
就会从新置为 0
重要事项:不具备标记 g 和不示意全局模式的 RegExp 对象不能应用 lastIndex 属性。
前面,如同 y 修饰符
也反对了。
lastIndex
是个 可读可写 的属性
正则的修饰符
s
修饰符
/s
示意将字符串视为单行来匹配。
const dom = `
<h1>123</h1>
`;
const reg = /.+/s;
console.log(dom.match(reg));
s 模式,会把字符串看一行来解析
[
0: "\n <h1>123</h1>\n"
groups: undefined
index: 0
input: "\n <h1>123</h1>\n"
]
\n
就解析进去了
i
修饰符
正则是辨别大小写的,如果是在 i 模式下,就不辨别大小写了
g
修饰符
全局匹配。
个别状况下,没有量词的状况下,匹配到一个,就不会持续匹配上来了。如果增加了 \g
,就会始终匹配上来满足条件的。
y
修饰符
y
修饰符的作用与 g
修饰符相似,也是全局匹配,后一次匹配都从上一次匹配胜利的下一个地位开始。
不同之处:g 修饰符只有残余地位中存在匹配就可,而 y 修饰符确保匹配必须从残余的第一个地位开始
在这里,了解 y 修饰符
和 g 修饰符
须要了解下面的lastIndex
正则属性
实例:
const str = 'aaa_aa_aaaa'
const reg_g = /a+/g
const reg_y = /a+/y
console.log('第一次')
console.log(reg_g.lastIndex) // 0
console.log(reg_y.lastIndex) // 0
console.log(reg_g.exec(str)) // aaa
console.log(reg_y.exec(str)) // aaa
console.log('第二次')
console.log(reg_g.lastIndex) // 3
console.log(reg_y.lastIndex) // 3
console.log(reg_g.exec(str)) // aa
console.log(reg_y.exec(str)) // null
console.log('第三次')
console.log(reg_g.lastIndex) // 6
console.log(reg_y.lastIndex) // 0
console.log(reg_g.exec(str)) // aaaa
console.log(reg_y.exec(str)) // aaa
第一次, y 修饰符
和 g 修饰符
是一样的,lastIndex
都从 0 变成了 3
第二次, g 修饰符
能够持续匹配前面的残余字符 aa
,所以lastIndex
从 3 变成了 6
然而 y 修饰符
须要的是残余字符 的第一个须要满足,很显然第二次的残余字符是不满足的,所以 匹配的值为 null
,lastIndex
也从 重置了,变成 0
第三次, y 修饰符
有从新开始匹配。
u
修饰符
正则的办法
字符串
的办法
match()
语法:
str.match(regexp)
参数:
一个正则表达式对象。如果传入一个非正则表达式对象,则会隐式地应用 new RegExp(obj)
将其转换为一个 RegExp
。如果你没有给出任何参数并间接应用 match() 办法,你将会失去一 个蕴含空字符串的 Array
:[“”]。
返回值:
- 如果有
g
修饰符,则返回所有匹配的信息,不会返回捕捉值 - 如果没有
g
修饰符,则返回一个残缺以及相干的捕捉值。
示例:
// 状况一:存在 g 修饰符
const str = "copyer";
console.log(str.match(/c/g)); // ['c']
// 状况二:不存在 g 修饰符
const str = "copyer";
console.log(str.match(/c/));
// 残缺的信息
[
0: "c" // 捕捉值
groups: undefined
index: 0
input: "copyer"
]
matchAll()
语法:
str.matchAll(regexp)
参数:
正则表达式对象。如果所传参数不是一个正则表达式对象,则会隐式地应用 new RegExp(obj)
将其转换为一个 RegExp
。
RegExp
必须是设置了全局模式 g
的模式,否则会抛出异样TypeError
。
返回值:
一个迭代器
实例:
const str = "abaca";
const matchs = str.matchAll(/a/g) // 必须是全局模式
// 返回值是一个迭代对象,应用 for...of 遍历
for(let key of matchs) {console.log(key)
}
// ['a', index: 0, input: 'abaca', groups: undefined]
// ['a', index: 2, input: 'abaca', groups: undefined]
// ['a', index: 4, input: 'abaca', groups: undefined]
这里获取所有捕捉值的办法。然而在 matchAll()
呈现之前,是通过正则办法 exec()
来实现同样的目标。在上面会介绍到。
replace()
语法:
str.replace(regexp|substr, newSubStr|function)
参数:
-
regexp
(pattern)一个
RegExp
对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。 -
substr
(pattern)一个将被
newSubStr
替换的字符串
。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。 -
newSubStr
(replacement)用于替换掉第一个参数在原字符串中的匹配局部的
字符串
。该字符串中能够内插一些非凡的变量名。 -
function
(replacement)一个用来创立新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的后果。
返回值:
不扭转原来的字符串,会返回一个新的字符串
实例:
// 状况一:参数为字符串:参数看为一个整体,匹配到的第一项会被替换
const str = "abaca";
const newStr = str.replace('a', 'f')
console.log(newStr) // fbaca
// 状况二:参数为正则:匹配到的字符被新的字符串替换
const str = "abaca";
const newStr = str.replace(/a/g, 'f')
console.log(newStr) // fbfcf
// 状况三: 第二个参数为一个函数,返回的字符串用来替换
const str = "abaca";
const newStr = str.replace(/a/g, () => {return '$#'})
console.log(newStr)
当然,这个函数不是这么简略,还有其余的用法。
search()
语法:
str.search(regexp)
参数:
一个 正则表达式(regular expression)
对象。如果传入一个非正则表达式对象 regexp
,则会应用 new RegExp(regexp)
隐式地将其转换为正则表达式对象。
返回值:
如果匹配胜利,则 search()
返回正则表达式在字符串中首次匹配项的索引; 否则,返回 -1
。
实例:
const str = "abaca";
console.log(str.search(/[b]/g)) // 1
console.log(str.search(/[d]/g)) // -1
split()
语法:
str.split([separator [, limit]])
参数:
-
separator
指定示意每个拆分应产生的点的字符串。
separator
能够是一个字符串或正则表达式。如果纯文本分隔符蕴含多个字符,则必须找到整个字符串来示意宰割点。如果在 str 中省略或不呈现分隔符,则返回的数组蕴含一个由整个字符串组成的元素。如果分隔符为空字符串,则将 str 原字符串中每个字符的数组模式返回。 -
limit
一个整数,限定返回的宰割片段数量。当提供此参数时,split 办法会在指定分隔符的每次呈现时宰割该字符串,但在限度条目已放入数组时进行。如果在达到指定限度之前达到字符串的开端,它可能依然蕴含少于限度的条目。新数组中不返回剩下的文本。
返回值:
返回源字符串以分隔符呈现地位分隔而成的一个 Array
实例:
const str = "abaca";
console.log(str.split('c')) // ['aba', 'a']
console.log(str.split('@')) // ['abaca']
console.log(str.split('')) // ['a','b','a','c','a']
console.log(str.split('a')) // ['','b','c','']
console.log(str.split('', 3)) // ['a','b','a']
形容:
- 找到分隔符后,将其从字符串中删除,并将子字符串的数组返回。
- 如果没有找到或者省略了分隔符,则该数组蕴含一个由整个字符串组成的元素。
- 如果分隔符为空字符串,则将 str 转换为字符数组。
- 如果分隔符呈现在字符串的开始或结尾,或两者都离开,别离以空字符串结尾,结尾或两者开始和完结。
留神:
如果 分隔符是蕴含捕捉括号的正则表达式,则每次分隔符匹配时,捕捉括号的后果(包含任何未定义的后果)将被拼接到输入数组中。然而,并不是所有浏览器都反对此性能。
const str = "abaca";
console.log(str.split(/(a)/)) // ['','a','b','a','c','a','']
正则
的办法
test()
语法:
regexObj.test(str)
参数:
-
str
用来与正则表达式匹配的字符串
返回值:
如果正则表达式与指定的字符串匹配,返回true
;否则false
。
实例:
let str = 'hello world!';
let result = /^hello/.test(str);
console.log(result); // true
exec()
语法:
regexObj.exec(str)
参数:
-
str
要匹配正则表达式的字符串。
返回值:
如果匹配胜利,exec()
办法返回一个数组(蕴含额定的属性 index
和 input
),并更新正则表达式对象的 lastIndex
属性。
齐全匹配胜利的文本将作为返回数组的第一项,从第二项起,后续每项都对应正则表达式内捕捉括号里匹配胜利的文本。
如果匹配失败,exec()
办法返回 null
,并将 lastIndex
重置为 0。
实例:
const str = "abaca";
const reg = /a/g
console.log(reg.lastIndex) // 0
console.log(reg.exec(str)) // ['a', index: 0, input: 'abaca', groups: undefined]
console.log(reg.lastIndex) // 1
lastIndex
会被更新。
获取全副的匹配信息:
let str = "abaca";
let reg = /a/g;
let result;
while ((result = reg.exec(str)) != null) {console.log(result)
}
// ['a', index: 0, input: 'abaca', groups: undefined]
// ['a', index: 2, input: 'abaca', groups: undefined]
// ['a', index: 4, input: 'abaca', groups: undefined]
谬误写法:
let str = "abaca";
let reg = /a/g;
let result;
while (reg.exec(str) !== null) {console.log(result)
}
正告: 不要把正则表达式字面量(或者
RegExp
结构器)放在while
条件表达式里。因为每次迭代时lastIndex
的属性都被重置,如果匹配,将会造成一个死循环。并且要确保应用了 ’g’ 标记来进行全局的匹配,否则同样会造成死循环。
正则的断言
正后行断言(?=
)
前面的内容,满足后面的条件
实例:
str='我有一只铅笔,我有一只钢笔'
,把铅笔后面的 一只,换成 2 只
let str = "我有一只铅笔,我有一只钢笔";
const reg = / 一只(?= 铅笔)/g
const newStr = str.replace(reg, '两只')
console.log(newStr) // 我有两只铅笔,我有一只钢笔
了解:
就看成是 条件 就行了,前面的内容 是否等于 ‘ 匹配的内容 ’。
在下面的例子中,想把 一只 改成 两只 ,全局搜寻的话,就会搜寻出两个,咱们只须要匹配 铅笔 的那一个,所以 判断条件 是否等于(?=
) 铅笔就行了
负后行断言(?!=
)
前面的内容,不满足后面的条件
实例:
let str = "我有一只铅笔,我有一只钢笔";
const reg = / 一只(?! 钢笔)/g
const newStr = str.replace(reg, '两只')
console.log(newStr) // 我有两只铅笔,我有一只钢笔
取反即可
正后发断言(?<=
)
后面的内容,满足前面的条件
实例:
let str = "我有一个大爸和二爸";
const reg = /(?<= 大)爸 /g
const newStr = str.replace(reg, '爷')
console.log(newStr) // 我有一个大爷和二爸
把 大爸 和 二爸
中的 大爸的爸改成爷(只是举例,其余形式更加的简略)
负后发断言(?<!
)
后面的内容,不满足前面的条件
实例:
let str = "我有一个大爸和二爸";
const reg = /(?<! 二)爸 /g
const newStr = str.replace(reg, '爷')
console.log(newStr) // 我有一个大爷和二爸
意思跟下面差不多
总结
断言:就看成 条件。
- 后行: 就是前面的内容须要满足的条件
/ 表达式(条件)/
条件是写在前面的
<!—->
- 后发: 就是后面的内容须要满足的条件
/(条件)/ 表达式
条件是写在后面的
留神:
应用了小括号,然而不能看成是 原子组
总结
花了两天的工夫,从头学习了一遍正则的根本语法。当然,在理论实际中,还是须要积攒的。
如果下面的内容有误,请提出来,多谢指教。