正则表达式
正则表达式是用来匹配字符串的一种模式。
做的一次分享,记录一下
规则:
最先匹配上的优先,例
'1wer123s'.replace(/1/,'') //"wer123s"
主要参考
- 以前学js记的笔记
- https://www.w3school.com.cn/jsref/jsref_obj_regexp.asp
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
工具型网站
正则大全:现成正则,平时写正则不多的,直接用这个也够了
创建
let reg = /abc/;
let reg = new RegExp("abc");
特殊字符(需要转义的字符)
普通字符包括:字母(abcABC),数字(123),_等等
特殊字符:() [] {} ^ $ * ? \ | + . 需要用\转义
let reg = /\$/;
let str = '15$';
reg.test(str); //true
修饰符g i m
放在正则//后面
- g 表示全局匹配
- i 忽略大小写
- m 多行匹配
let reg = /1/g;
let str = '1516';
str.split(reg); // ["", "5", "6"]
分界符^ $ ?= ?!
-
^ 以某字符开头
-
$ 已某字符结尾
let reg = /^1/; let str = '16+19'; reg.test(str); // true
字符 | 含义 |
---|---|
^n | 以n开头 |
n$ | 以n结尾 |
分组()和|或
|表示或,如果分组中没有|,就必须匹配分组内完整的字符串
let reg = /^(1|2)/;
let str = '22$15';
reg.test(str); //true
let reg = /^(12)/;
let str = '22$15';
reg.test(str); // false
当正则有使用分组时,可以通过RegExp. 1 取 正 则 对 应 分 组 的 内 容 , 1取正则对应分组的内容, 1取正则对应分组的内容,后跟的数字表示是匹配的正则中第几个分组。
let reg = /^(1)\d*(9)$/;
let str = '1345789';
reg.test(str); //true
RegExp.$1 // '1'
RegExp.$2 // '9'
非捕获括号?:
匹配 ‘x’ 但是不记住匹配项。这种括号叫作非捕获括号,使得你能够定义与正则表达式运算符一起使用的子表达式。
捕获
let reg = /^(1)\d*(9)$/;
let str = '1345789';
reg.exec(str); // ['1345789', '1', '9', index: 0, input: '1345789', groups: undefined]
reg.test(str); // true
RegExp.$1 // '1'
RegExp.$2 // '9'
非捕获
let reg = /^(?:1)\d*(9)$/;
let str = '1345789';
reg.exec(str); // ['1345789', '9', index: 0, input: '1345789', groups: undefined]
reg.test(str); // true
RegExp.$1 // '9',
RegExp.$2 // ''
断言
来自MDN
模式 | 含义 |
---|---|
x(?=y) | x后紧跟y, 匹配’x’仅仅当’x’后面跟着’y’.这种叫做先行断言。 例如,/Jack(?=Sprat)/会匹配到’Jack’仅当它后面跟着’Sprat’。/Jack(?=Sprat|Frost)/匹配‘Jack’仅当它后面跟着’Sprat’或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。 |
(?<=y)`x | x前紧跟y, 匹配’x’仅当’x’前面是’y’.这种叫做后行断言。 例如,/(?<=Jack)Sprat/会匹配到’ Sprat ‘仅仅当它前面是’ Jack '。/(?<=Jack|Tom)Sprat/匹配‘ Sprat ’仅仅当它前面是’Jack’或者是‘Tom’。但是‘Jack’和‘Tom’都不是匹配结果的一部分。 |
x(?!y) | x后不跟y, 仅仅当’x’后面不跟着’y’时匹配’x’,这被称为正向否定查找。 例如,仅仅当这个数字后面没有跟小数点的时候,/\d+(?!.)/ 匹配一个数字。正则表达式/\d+(?!.)/.exec(“3.141”)匹配‘141’而不是‘3.141’ |
(?<!y)x | x前不跟y, 仅仅当’x’前面不是’y’时匹配’x’,这被称为反向否定查找。 例如, 仅仅当这个数字前面没有负号的时候,/(?<!-)\d+/ 匹配一个数字。 /(?<!-)\d+/.exec('3') 匹配到 “3”. /(?<!-)\d+/.exec('-3') 因为这个数字前有负号,所以没有匹配到。 |
let str = 'foofoo';
let reg1 = /foo{2,3}/;
let reg2 = /(?:foo){2,3}/;
reg1.test(str); // false
reg2.test(str); // true
先行断言(前瞻)
符号 | 含义 |
---|---|
x(?=y) | x其后紧跟 y |
x(?!y) | x后不跟y |
let str="Is this all there is";
let reg=/is(?= all)/g;
reg.test(str) // true
let str="Is this all there is";
let reg=/is(?=all)/g;
reg.test(str) // false
后发断言(后顾)
符号 | 含义 |
---|---|
(?<=y)x | x前紧跟y |
(?<!y)x | x前不跟y |
量词? * + {}
符号 | 出现次数(x) |
---|---|
? | 0 || 1 {0,1} |
* | x>=0 {0,} |
+ | x>=1 {1,} |
{n} | 0 || n |
{n,m} | n<= x <=m (中间不能有空格) |
{n,} | x >= n |
let reg = /\$+/;
let str = '15$16$$';
str.split(reg); // ["15", "16", ""]
模式
参考较多
性能优化探究影响性能的因素NFA自动机的回溯
注意:
1、同贪婪模式一样,独占模式一样会匹配最长。不过在独占模式下,正则表达式尽可能长地去匹配字符串,一旦匹配不成功就会结束匹配而不会回溯,可用于性能优化
2、js不支持独占模式,会报错
贪婪(量词) | 懒惰(?) | 独占(+) |
---|---|---|
X? | X?? | X?+ |
X* | X*? | X*+ |
X+ | X+? | X++ |
X{n} | X{n}? | X{n}+ |
X{n,} | X{n,}? | X{n,}+ |
X{n,m} | X{n,m}? | X{n,m}+ |
贪婪模式(最长匹配).+ , .*
1、量词默认贪婪模式(会先匹配最长的,匹配不上再回溯,往前推。所以量词要避免嵌套否则会导致指数级回溯)
let reg = /b{1,3}/;
let str = 'aaabbc';
str.match(reg) // ['bb', index: 3, input: 'aaabbc', groups: undefined]
2、/a.*b/表示匹配a到b的最长字符串
let reg = /a.*b/;
let str = '15a$a16b$b$bc';
str.split(reg); // ["15", "c"]
str.replace(reg, '-'); //'15-c'
懒惰模式(最短匹配).+? , .*?
1、量词后加?,变成懒惰模式
let reg = /b{1,3}?/;
let str = 'abbc';
reg.test(str) //true
let reg = /b{1,3}?/; // 懒惰
let str = 'aaabbc';
str.match(reg) // 先匹配最短['b', index: 3, input: 'aaabbc', groups: undefined]
let reg = /b{1,3}/; // 贪婪
let str = 'aaabbbc';
str.match(reg) // 先匹配最长['bbb', index: 3, input: 'aaabbbc', groups: undefined]
2、/a.*?b/表示匹配a到b的最短字符串
let reg = /a.*?b/;
let str = '15a$a16b$b$bc';
str.split(reg); // ["15", "$b$bc"]
字符集[]
只要符合集合中的一项即可
基础
let reg = /[a13kc]/;
let str = '12$15';
reg.test(str); //true
范围字符集
常用
- [a-z] 小写子母集合,也可以从中间开始取[b-d]表示bcd
- [A-Z] 大写子母集合
- [0-9] 数字集合
特殊含义字符
^非
[^123] 表示不包含1的字符集
\b退格符
[\b]表示退格符(非元字符的单词边界)
注意:
除了 \,-,^, ] 之外的非字母数字字符在字符类中都没有特殊含义,不需要转义
组合拼接[0-9]
常用
[a-zA-Z] 子母集合
[\u4e00-\u9fa5] 中文集合 (汉字Unicode编码)
元字符(Metacharacter)
特殊字符(换行等)\t \n \f …
***注意:***空格符没有特殊字符,直接输入空格即可
let reg = / /g;
let str = `a b`;
reg.test(str); // true
let reg = / /g;
let str = `ab`;
reg.test(str); // false
符号 | 含义 |
---|---|
\t | 制表符 |
\n | 换行符 |
\f | 换页符 |
\r | 回车符 |
\0 | NUL 字符 |
\v | 垂直制表符 |
\xxx | 以八进制数 xxx 规定的字符 |
\xdd | 查找以十六进制数 dd 规定的字符。 |
\uxxxx | 以十六进制数 xxxx 规定的 Unicode 字符 |
let reg = /\n/;
let str = `a
b`;
reg.test(str); // true
类 (特殊字符集缩写). \d \s \w …
类 | 字符集 | 含义 |
---|---|---|
. | [^\n\r] | 非换行、回车符 |
\d | [0-9] | 数字 |
\D | [^0-9] | 非数字 |
\s | [ \t\n\x0B\f\r] | 空白(\x0B表示垂直tab,无法书写),含空格 |
\S | [^ \t\n\x0B\f\r] | 非空白 |
\w | [a-zA-Z_0-9] | 单词字符(字母数字下划线) |
\W | [^a-zA-Z_0-9] | 非单词 |
\b | - | 单词边界(单词字符的边界)例:2a+3b=5c,边界就是2a,3b,5c左右两边的位置 |
\B | - | 非单词边界,如上的例子,非单词边界就是2和a,3和b,5和c之间的位置 |
let reg = /\d/;
let str = '15$';
reg.test(str); //true
let str = 'm13';
let reg1 = /\bm1/; // \b放左就表示匹配字符m1其左边是边界
let reg2 = /m1\b/; // \b放右就表示匹配字符m1其右边是边界
str.match(reg);
reg1.test(str); // true,
reg2.test(str); //false,
性能
正则也是存在性能问题的,某些优化不好的正则会不断回溯,占用cpu很长时间去匹配,影响页面效果。
改进:避免回溯
console.time('start');
console.log(/(A+A+)+B/.exec('AAAAAAAAAA')); // 量词嵌套导致回溯
console.timeEnd('end')
1、在使用量词匹配时尽量使用[最长](#贪婪模式(最长匹配).+ , .*)或[最短](#懒惰模式(最短匹配).+? , .*?)匹配
js正则相关方法
传入方法的数字一般会匹配去对应的字符串
RegExp 对象方法
test
含有与 RegExpObject
匹配的文本,则返回 true,否则返回 false。
RegExpObject.test(string)
exec
详解:w3c
索字符串中的正则表达式的匹配。返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。
RegExpObject.exec(string)
/\d/.exec('21ssfr')
["2", index: 0, input: "21ssfr", groups: undefined]
注意:
此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject
的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject
的第 2 个子表达式相匹配的文本(如果有的话),以此类推
即返回的只有第0个元素才是完全匹配的文本,后面两个都只是子表达式
(括号中的分组捕获)匹配的结果,具体案例见文章最后vue3源码正则-获取当前tag
compile
egExpObject.compile(regexp,modifier)
参数 | 描述 |
---|---|
regexp | 正则表达式。 |
modifier | 规定匹配的类型。“g” 用于全局匹配,“i” 用于区分大小写,“gi” 用于全局区分大小写的匹配。 |
var str="Every man in the world! Every woman on earth!";
patt=/man/g;
str2=str.replace(patt,"person");
document.write(str2+"<br />"); // Every person in the world! Every woperson on earth!
patt=/(wo)?man/g;
patt.compile(patt);
str2=str.replace(patt,"person"); // Every person in the world! Every person on earth!
document.write(str2);
String 对象方法
search
stringObject.search(regexp|string)
返回值
stringObject 中第一个与 regexp 相匹配的子串的起始位置。
注释:如果没有找到任何匹配的子串,则返回 -1。
说明
search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置
let str = '11te3st123'
let reg = /1/;
str.search(reg); // 0
match
match() 方法可在字符串内检索指定的值,或找到一个或多个(正则有无全局匹配g标识)正则表达式的匹配。
该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。
stringObject.match(string | regexp)
例
let str = 'test123';
let reg = /^test/;
str.match(reg); // ["test", index: 0, input: "test123", groups: undefined]
let searchStr = 'st';
str.match(searchStr); // ["st", index: 2, input: "test123", groups: undefined]
let str = '11te3st123';
let reg = /1/g;
str.match(reg); // ["1", "1", "1"]
replace
https://www.w3school.com.cn/jsref/jsref_replace.asp
replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
stringObject.replace(regexp/substr,replacement)
replacement: 字符串或函数
特殊字符$:
字符 | 替换文本 |
---|---|
$1、$2、…、$99 | 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。 |
$& | 与 regexp 相匹配的子串。 |
$` | 位于匹配子串左侧的文本。 |
$’ | 位于匹配子串右侧的文本。 |
$$ | 直接量符号。 |
let reg = /(\w+)\+(\w+)/;
let str = 'a+b';
str.replace(reg, '$2, $1'); // "b, a"
console.log(RegExp.$1); // "a"
console.log(RegExp.$2); // "b"
split
split() 方法用于把一个字符串分割成字符串数组。
stringObject.split(string|reg,howmany)
howmany: 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
let str = '11te3st123';
let reg = /1/
str.split(reg, 3); // ["", "", "te3st"].length <= 3
正则案例
电话:
/^1[3-9]\d{9}$/
网址:
/^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/
邮箱:
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
问题:用正则怎么解析类似vue的模板变量
举个栗子,实际源码不是这样解析的
let str = '嗨,您好,今天是星期 {{ day.value }}';
let reg = /\{\{.+?\}\}/;
reg.test(str);
let str = '嗨,您好,今天是星期 {{ day.value }},天气{{ day.weather }}';
let reg = /\{\{.+?\}\}/g;
let arr = str.match(reg); // ['{{ day.value }}', '{{ day.weather }}']
arr.map(a => a.replaceAll(/[\{\}\s]/g, '')) // ['day.value', 'day.weather']
网址:
/^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/
邮箱:
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
问题:用正则怎么解析类似vue的模板变量
举个栗子,实际源码不是这样解析的
let str = '嗨,您好,今天是星期 {{ day.value }}';
let reg = /\{\{.+?\}\}/;
reg.test(str);
let str = '嗨,您好,今天是星期 {{ day.value }},天气{{ day.weather }}';
let reg = /\{\{.+?\}\}/g;
let arr = str.match(reg); // ['{{ day.value }}', '{{ day.weather }}']
arr.map(a => a.replaceAll(/[\{\}\s]/g, '')) // ['day.value', 'day.weather']