JavaScript RegExp
前言
正则表达式其本身就是一种小型的,高度专业化的编程语言,能够非常方便的对字符串进行处理。
正则语法在各个语言中都是通用的,所以掌握它显得十分有必要。
创建正则
在Js中,提供两种创建正则的方式。
字面量创建
使用//包裹的字面量创建方式是推荐的作法,但它不能在其中使用变量。
<script> "use strict"; let str = "hello,world"; let reg = /^h\w+,\w+$/; // 创建正则对象 console.log(reg.test(str)); // true </script>
下面尝试使用 a 变量时将不可以查询.
可以使用 eval() 转换为Js语法来实现将变量解析到正则中,但是比较麻烦,所以有变量时建议使用下面的构造函数创建方式。
<script>"use strict"; let str = "hello,world"; let a = "hello"; console.log(eval(`/${a}/`).test(str)); // true 验证字符串中是否有hello </script>
构造函数创建
使用构造函数创建对象。
<script>"use strict"; let str = "hello,world"; let reg = new RegExp(/^h\w+,\w+$/) console.log(reg.test(str)); // true </script>
当正则需要动态创建时使用这种方式。
<script>"use strict"; let str = "hello,world"; let a = "hello"; let reg = new RegExp(a); console.log(reg.test(str)); // true </script>
基础语法
. 通配符
代表匹配除\n外的任意字符。如果想匹配\n可使用s模式。
<script>"use strict"; let str = "hello,world\nJavaScript-RegExp"; let reg = /........../g; // 匹配十个除开\n外的任意字符 console.log(str.match(reg)); // (2) ["hello,worl", "JavaScript"] </script>
^ 开始符
代表必须以某个字符开始。
<script>"use strict"; let str = "hello,world\nJavaScript-RegExp"; let reg = /^hello,...../g; // 必须以h开始,后面必须是ello, 然后匹配五个除开\n的任意字符。 console.log(str.match(reg)); // ["hello,world"] </script>
$ 结束符
代表必须以某个字符结束。
<script>"use strict"; let str = "hello,world\nJavaScript-RegExp"; let reg = /Exp$/g; // 必须以p结束,前面两个字符必须是E与x console.log(str.match(reg)); // ["Exp"] </script>
重复符
* 重复符
代表可以取0到无穷位 (默认贪婪取值,可通过?取消贪婪模式)。
<script>"use strict"; let str_1 = "hello,world\nJavaScript-RegExp"; let str_2 = "h\np"; let reg = /^h.*\n.*p$/g; // 必须是以h开头,可以有0个也可以有多个除\n外的任意字符,紧接\n,可以有0个也可以有多个除\n外的任意字符,必须以p结尾。 console.log(str_1.match(reg)); // ["hello,world↵JavaScript-RegExp"] console.log(str_2.match(reg)); // ["h↵p"] 上面条件只有三个是必须的 h开头,p结束,中间必须有换行\n。所以str_2也能匹配出来 </script>
+ 重复符
代表可以取1到无穷位 (默认贪婪取值,可通过?取消贪婪模式)。
<script>"use strict"; let str_1 = "hello,world\nJavaScript-RegExp"; let str_2 = "h\np"; let reg = /^h.+\n.+p$/g; // 必须是以h开头,必须有1个也可以有多个除\n外的任意字符,紧接\n,必须有1个也可以有多个除\n外的任意字符,必须以p结尾。 console.log(str_1.match(reg)); // ["hello,world↵JavaScript-RegExp"] console.log(str_2.match(reg)); // null 不满足条件,至少有一个 </script>
? 重复符
代表可以取0到1位(默认贪婪取值,可通过?取消贪婪模式)。
<script>"use strict"; let str_1 = "123456789"; let str_2 = "23456789"; let reg = /^1?23456789$/g; // 可以以1开头,也可以不以,后面是23456789,必须是9结尾 console.log(str_1.match(reg)); // ["123456789"] console.log(str_2.match(reg)); // ["23456789"] </script>
{n,m} 重复符
代表匹配n到m次由前面的正则表达式定义的片段(默认贪婪取值,可通过?取消贪婪模式)。
<script>"use strict"; let str_1 = "1111111"; let reg = /1{3,4}/g; // 可以是3个1,也可以是4个1 console.log(str_1.match(reg)); // ["1111", "111"] </script>
{n} 精确重复
精确匹配n个前面的表达式。
<script>"use strict"; let str_1 = "1111111"; let reg = /1{3}/g; // 3个1 console.log(str_1.match(reg)); // ["111", "111"] </script>
取消贪婪
使用?取消贪婪。
<script>"use strict"; let str_1 = "1111111"; let reg_1 = /1*?/g; // 0个或者多个,取消贪婪后不取 let reg_2 = /1+?/g; // 1个或者多个,取消贪婪后取1个 let reg_3 = /1??/g; // 0个或者1个,取消贪婪后不取 let reg_4 = /1{3,4}?/g; // 3个或者4个,取消贪婪后取3个 console.log(str_1.match(reg_1)); // ["", "", "", "", "", "", "", ""] console.log(str_1.match(reg_2)); // ["1", "1", "1", "1", "1", "1", "1"] console.log(str_1.match(reg_3)); // ["", "", "", "", "", "", "", ""] console.log(str_1.match(reg_4)); // ["111", "111"] </script>
字符集
在字符集中上面的字符均失去原本含义。只有- ^ \ 可以在字符集中使用。
[] 字符集
其本身代表或的作用 [ab]代表a或者b。
<script>"use strict"; let str_1 = "1111111"; let str_2 = "2222222"; let reg_1 = /[12]+/g; // 1或者2,取多个 console.log(str_1.match(reg_1)); // ["1111111"] console.log(str_2.match(reg_1)); // ["2222222"] </script>
[-] 区间符
字符集中的 - 号代表可以取从多少到多少区间的值,ASCII码排序,比如 [a-z 0-9 A-Z]就是取全部的英文字母和数字。
<script>"use strict"; let str_1 = "12345abcde"; let reg_1 = /[a-z0-9]+/g; // 字符可以是a-z或者0-9,取多个 console.log(str_1.match(reg_1)); // ["12345abcde"] </script>
[^] 排除符
字符集中的 ^ 号代表 非 的作用。比如[^0-9]就是说这一位数并非数字。
<script>"use strict"; let str_1 = "12345abcde"; let str_2 = "x2345abcde"; let reg_1 = /^[^0-9].+/g; // 不能以0-9开头,紧跟着取处\n外的任意字符 console.log(str_1.match(reg_1)); // null console.log(str_2.match(reg_1)); // ["x2345abcde"] </script>
[\] 转义符
除开在字符集中使用还可以在外部使用,它可以使所有具有特殊意义的字符失去特殊意义。并且还可以为特定的字符指定意义。
<script>"use strict"; let str_1 = "\t\n\t\n"; let reg_1 = /[\t\n]+/g; // 取\t或\n console.log(str_1.match(reg_1)); // [" ↵ ↵"] </script>
其他的转义符会在下面介绍。这里举例只代表\可以在字符集中使用。
\ 转义符
转义符除开在字符集中使用,也可以在外部使用。
| \d | 匹配任何十进制数,它相当于在字符集中使用[0-9] |
| \D | 匹配任何非十进制数,它相当于在字符集中使用[^0-9] |
| \s | 匹配任何空白字符,它相当于在字符集中使用[\t\n\r\f\v] |
| \S | 匹配任何非空白字符,它相当于在字符集中使用[^\t\n\r\f\v] |
| \w | 匹配任何字母数字下划线字符,它相当于在字符集中使用[a-z A-Z 0-9] |
| \W | 匹配任何非字母数字下划线字符,它相当于在字符集中使用[a-z A-Z 0-9] |
| \b | 匹配一个特殊字符边界,比如空格,&.#等(不常用) |
| \A | 匹配字符串开始(不常用) |
| \Z | 匹配字符串结束,如果存在换行则只匹配换行前的字符(不常用) |
| \z | 匹配字符串结束(不常用) |
| \G | 匹配最后匹配完成的位置(不常用) |
| \n | 匹配一个换行符(不常用) |
| \t | 匹配一个制表符(不常用) |
| \f | 匹配一个分页符(不常用) |
| 管道符
相当于 或 请注意与字符集里的区别。|符将前后分为两段,左右看做一个整体,而[]中的或仅仅代表从众多选项中取出一个。
<script>"use strict"; let str_1 = "abcdefabcccde"; let reg_1 = /abc|def/g; // 取abc或者def console.log(str_1.match(reg_1)); // ["abc", "def", "abc"] </script>
分组
() 分组符
将多个元素字符看出一个元素字符进行匹配。
<script>"use strict"; let str_1 = "asdfghjkl"; let str_2 = "asbfghjkl"; let reg_1 = /^(asd)\w+l$/g; // 必须以asd开头,必须以l结尾 console.log(str_1.match(reg_1)); // ["asdfghjkl"] console.log(str_2.match(reg_1)); // null </script>
分组别名
组别名使用 (?<别名>匹配规则) 形式定义。在使用match检测时需要取消g模式的全局匹配。
<script>"use strict"; let str_1 = "http://www.google.com/"; let reg_1 = /^https?:\/\/w{3}\.(?<name>\w+)\.(com|cn|org|hk)/; console.log(str_1.match(reg_1)); // ["http://www.google.com", "google", "com", index: 0, input: "http://www.google.com/", groups: {name: "google"}] console.dir(str_1.match(reg_1).groups.name); // google </script>
分组引用
每一个分组都有一个编号,如1,2,3,4,5。
\num 在匹配时引用原子组(匹配到的具体内容,注意不是规则), $num 指在替换时使用匹配的组数据。
如下所示,将所有的<h1-6>标签替换为<p>标签,使用编号替换。
<script>"use strict"; let str_1 = ` <h1>hello,world</h1> <h2>hello,JavaScript</h2> <h3>PHP,no.1!!!</h3> `; let reg = /<(?<grade>h[1-6])>(?<content>[\s\S]*?)\/\1/gi; // 最后的\1代表引用第一个分组的内容,即grade组 [\s\S]代表匹配任意 let new_str = str_1.replace(reg,`<p>$2</p>`); // $2 代表拿到第二个分组的内容,即content分组。 console.log(new_str); // <p>hello,world<</p>> // <p>hello,JavaScript<</p>> // <p>PHP,no.1!!!<</p>> </script>
也可以使用别名替换。$<别名>。
let new_str = str_1.replace(reg,`<p>$<content></p>`); // 拿到content分组
console.log(new_str);
记录取消
通常情况下,我们的分组会记录编号方便后面使用。
如果一个分组的内容编号不想被记录,可使用(?:)进行处理。
<script>"use strict"; let str_1 = "123,abc,456"; let reg_1 = /(?:\d+),([a-z]+),(\w+)/; let reg_2 = /(\d+),([a-z]+),(\w+)/; console.log(str_1.match(reg_1)); // (3) ["123,abc,456", "abc", "456", index: 0, input: "123,abc,456", groups: undefined] console.log(str_1.match(reg_2)); // (4) ["123,abc,456", "123", "abc", "456", index: 0, input: "123,abc,456", groups: undefined] // 可以看到。reg_2的长度多了一位,记录了第一个分组。 </script>
断言匹配
断言虽然写在扩号中但它不是组,所以不会在匹配结果中保存,可以将断言理解为正则中的条件。
(?=exp)
零宽先行断言 ?=exp 匹配后面为 exp 的内容。
<script>"use strict"; let str_1 = "hello,JavaScript"; let reg_1 = /\w+(?=,JavaScript)/g; // 匹配出后面是,JavaScript的内容 console.log(str_1.match(reg_1)); // ["hello"] </script>
(?<=exp)
零宽后行断言 ?<=exp 匹配前面为 exp 的内容。
<script>"use strict"; let str_1 = "hello,JavaScript"; let reg_1 = /(?<=hello,)\w+/g; // 匹配出前面是hello,的内容 console.log(str_1.match(reg_1)); // ["JavaScript"] </script>
(?!exp)
零宽负向先行断言 后面不能出现 exp 指定的内容。
<script>"use strict"; let str_1 = "_123"; let str_2 = "yun23"; let str_3 = "123"; let reg_1 = /^(?!_)[\w]+/g; // 匹配开始不能是下划线的 console.log(str_1.match(reg_1)); // null console.log(str_2.match(reg_1)); // ["yun23"] console.log(str_3.match(reg_1)); // ["123"] </script>
(?<!exp)
零宽负向后行断言 前面不能出现exp指定的内容
<script>"use strict"; let str_1 = "abc123 456"; let reg_1 = /(?<!\s+)\d{3}/g; // 前面不能是空格的三位数字 console.log(str_1.match(reg_1)); // ["123"] </script>
匹配模式
正则表达式在执行时会按他们的默认执行方式进行,但有时候默认的处理方式总不能满足我们的需求,所以可以使用模式修正符更改默认方式。
| 修饰符 | 说明 |
|---|---|
| i | 不区分大小写字母的匹配 |
| g | 全局搜索所有匹配内容 |
| m | 视为多行 |
| s | 视为单行忽略换行符,使用. 可以匹配所有字符 |
| y | 从 regexp.lastIndex 开始匹配 |
| u | 正确处理四个字符的 UTF-16 编码 |
i
i模式匹配时不区分大小写。
<script>"use strict"; let str_1 = "HELLO,WORLD"; let reg_1 = /hello,world/i; console.log(str_1.match(reg_1)); // ["HELLO,WORLD", index: 0, input: "HELLO,WORLD", groups: undefined] </script>
g
g模式不会在第一次匹配成功后就停止,而是继续向后匹配,直到匹配完成。
<script>"use strict"; let str_1 = "HELLO,WORLD"; let reg_1 = /./g; console.log(str_1.match(reg_1)); // (11) ["H", "E", "L", "L", "O", ",", "W", "O", "R", "L", "D"] </script>
m
m模式会将每一行单独匹配,用于将内容视为多行匹配,主要是对 ^和 $ 的修饰
<script>"use strict"; let str_1 = ` # 1.HTML5 # # 2.CSS # # 3.JavaScript # `; let reg_1 =/^\s+# \d\.\w+ #$/gm; // g模式,全局,m模式,多行。每一行必须是多个空格开头,#号结尾。 console.log(str_1.match(reg_1)); // ["↵↵ # 1.HTML5 #", " # 2.CSS #", " # 3.JavaScript #"] </script>
s
s模式将多行以一行匹配,忽略换行符\n。这代表使用.可匹配到任意字符。
<script>"use strict"; let str_1 = `123\n456\n789`; let reg_1 =/.*/gs; console.log(str_1.match(reg_1)); // ["123↵456↵789", ""] </script>
u
每个字符都有属性,如L属性表示是字母,P 表示标点符号,需要结合 u 模式才有效。其他属性简写可以访问
如果是一些特殊的生僻字符,那么它的字节宽度可能是4字节或更多。此时就无法正确解释出来,使用u模式可解决该问题。
<script>"use strict"; let str_1 = "𝒳𝒴"; let reg_1 =/𝒳𝒴/g; let reg_2 =/𝒳𝒴/gu; console.log(str_1.match(reg_1)); // 结果为乱字符"�" console.log(str_1.match(reg_1)); // ["𝒳𝒴"] </script>
字符也有unicode文字系统属性 Script=文字系统,下面是使用 \p{sc=Han} 获取中文字符 han为中文系统,其他语言请查看
<script>"use strict"; let str_1 = "云崖先生"; let reg_1 = /\p{sc=Han}+/gu; console.log(str_1.match(reg_1)); // ["云崖先生"] </script>
lastIndex
RegExp对象lastIndex 属性可以返回或者设置正则表达式开始匹配的位置。
必须结合
g修饰符使用对
exec方法有效匹配完成时,
lastIndex会被重置为0
<script>"use strict"; let str_1 = "Hello,My name is YunYa,YunYa age is 19"; let reg_1 = /YunYa.+/g; reg_1.lastIndex = 23; // 从索引23处开始匹配。 console.log(reg_1.exec(str_1)); // ["YunYa age is 19", index: 23, input: "Hello,My name is YunYa,YunYa age is 19", groups: undefined] </script>
y
g模式无论成功与否都会一直向后匹配,直到lastIndex等于length。
而y模式也是一直向后匹配,但是只要匹配不成功就会停止。
<script>"use strict"; let str_1 = "1211111"; let reg_1 = /1/g; let reg_2 = /1/y; console.log(reg_1.exec(str_1)); // ["1", index: 0, input: "1211111", groups: undefined] console.log(reg_2.exec(str_1)); // ["1", index: 0, input: "1211111", groups: undefined] console.log(reg_1.lastIndex); // 1 console.log(reg_2.lastIndex); // 1 console.log(reg_1.exec(str_1)); // ["1", index: 2, input: "1211111", groups: undefined] console.log(reg_2.exec(str_1)); // null console.log(reg_1.lastIndex); // 3 没找到,g模式继续 console.log(reg_2.lastIndex); // 0 没找到,y模式归零 </script>
字符串方法
以下字符串方法都可以和正则搭配使用。
search
search() 方法用于检索字符串中指定的子字符串的索引值,也可以使用正则表达式搜索,返回值为索引位置
<script>"use strict"; let str_1 = "YunYaSir c2323182108@gmail.com"; let reg_1 = /c\d+@\w+.?\w+?\.com/; console.log(str_1.search(reg_1)); // 9 </script>
match
match()方法可以返回出匹配到的子字符串。
<script>"use strict"; let str_1 = "YunYaSir c2323182108@gmail.com"; let reg_1 = /c\d+@\w+.?\w+?\.com/; console.log(str_1.match(reg_1)); // ["c2323182108@gmail.com", index: 9, input: "YunYaSir c2323182108@gmail.com", groups: undefined] // 查找到的内容 索引位置 被查找的字符串 分组信息 </script>
如果使用g模式,返回的内容没那么详细了。
<script>"use strict"; let str_1 = "YunYaSir c2323182108@gmail.com"; let reg_1 = /c\d+@\w+.?\w+?\.com/g; console.log(str_1.match(reg_1)); // ["c2323182108@gmail.com"] </script>
matchAll
match()如果不是在g模式下,那么只会检索一次。
而matchAll()是全局检索,返回一个可迭代对象。
<script>"use strict"; let str_1 = "YunYaSir c2323182108@gmail.com c238923874@qq.com"; let reg_1 = /c\d+@\w+.?\w+?\.com/g; let res = str_1.matchAll(reg_1); for (const iterator of res) { console.log(iterator); } </script>
split
用于使用字符串或正则表达式分隔字符串。
如下示例,时间的分隔符不确定是那个,此时可以使用正则匹配。
<script>"use strict"; let str_1 = "2020-08/09"; let reg_1 = /[-/]/g; console.log(str_1.split(reg_1)); // (3) ["2020", "08", "09"] </script>
replace
replace() 方法不仅可以执行基本字符替换,也可以进行正则替换,下面替换日期连接符
<script>"use strict"; let str_1 = "2020-08/09"; let reg_1 = /[-/]/g; console.log(str_1.replace(reg_1,"-")); // 2020-08-09 </script>
字符串替换
替换字符串可以插入下面的特殊变量名:
| 变量 | 说明 |
|---|---|
$$ |
插入一个 "$"。 |
$& |
插入匹配的子串。常用 |
$` |
插入当前匹配的子串左边的内容。 |
$' |
插入当前匹配的子串右边的内容。 |
$n |
假如第一个参数是 RegExp 对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始 |
将百度一下四个文字加上链接:
<body>
<div>百度一下,你就知道</div>
</body>
<script>"use strict";
let div = document.querySelector("div");
div.innerHTML = div.innerHTML.replace("百度一下","<a href='http://www.baidu.com'>$&</a>")
</script>
回调函数
replace()支持回调函数操作,用于处理复杂的替换逻辑。
| 变量名 | 代表的值 |
|---|---|
| match | 匹配的子串。(对应于上述的$&。) |
| p1,p2, ... | 假如replace()方法的第一个参数是一个 RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+,p2 就是匹配的 \b+。 |
| offset | 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1) |
| string | 被匹配的原字符串。 |
| NamedCaptureGroup | 命名捕获组匹配的对象 |
使用回调函数将百度一下四个字加上链接:
<body>
<div>百度一下,你就知道</div>
</body>
<script>"use strict";
let div = document.querySelector("div");
div.innerHTML = div.innerHTML.replace("百度一下",(search,pos,source)=> `<a href='http://www.baidu.com'>${search}</a>`)
</script>
正则方法
下面是 RegExp 正则对象提供的操作方法
test
返回布尔值,是否验证成功。
检测输入的邮箱是否合法:
<body> <input type="text" name="email" /> </body> <script> let email = document.querySelector(`[name="email"]`); email.addEventListener("keyup", e => { console.log(/^\w+@\w+\.\w+$/.test(e.target.value)); }); </script>
exec
不使用 g 修饰符时与 match 方法使用相似,使用 g 修饰符后可以循环调用直到全部匹配完。
使用
g修饰符多次操作时使用同一个正则,即把正则定义为变量使用使用
g修饰符最后匹配不到时返回null
计算JavaScript共出现了几次。
<body>
<div>JavaScript的事件循环模型与许多其他语言不同的一个非常有趣的特性是,它永不阻塞。JavaScript非常优秀!</div>
</body>
<script>"use strict";
let txt = document.querySelector("div").innerHTML;
let reg = /(?<tag>JavaScript)/gi;
let num = 0;
while (reg.exec(txt)) {
num++;
}
console.log(`JavaScript共出现:${num}次。`);
</script>

浙公网安备 33010602011771号