贪婪模式和非贪婪模式
所有语言中正则都是默认为贪婪模式的
通过后追加 ? 将贪婪模式转化为非贪婪模式
如
// 贪婪模式
const str = 'aaaaa';
const reg = /\w?/g;
console.log(str.match(reg)); // ['a', 'a', 'a', 'a', 'a', '']
// 非贪婪模式
const str = 'aaaaa';
const reg = /\w??/g;
console.log(str.match(reg)); // [ '', '', '', '', '', '' ]
因为非贪婪模式 能匹配少 就不多匹配 ,?是匹配0次或1次。
匹配方式:
1、字符串从左到右,依次先匹配多,在匹配少,如果一旦匹配上了就不回头匹配。
2、贪婪匹配规则:能匹配上多个就不匹配少个。能匹配多 就不匹配少
基础
1、两种使用方式。构造函数和对象字面量
// 构造函数 规则为变量时,推荐使用(动态声明规则)
const reg = new RegExp('规则', '修饰符属性');
// 对象字面量 通常使用这种方式声明正则
const reg = /规则/修饰符属性;
2、修饰符属性
i:是否大小写 ignore case
g:是否全局匹配,否则只返回第一个匹配成功的字符 global
m:是否换行匹配 multi-line
3、匹配方式:
匹配的是大小写敏感且连续的字符串片段。
规则中一位代表匹配字符串中的一位
只要匹配成功,后面的内容就不会在匹配,否则会一次匹配。
const reg = /[0-9][0-9][0-9]/g;
const str = '09e000u00e000';
console.log(str.match(reg));
// [000, 000]
4、表达式 [] 一个[]代表匹配一位
const reg = /test/;
const str = 'this is a test';
console.log(str.match(reg));
// [test]
// 理解 取的是每一个[]的任意一项,也就是每一个[]进行组合匹配,就会有 wxz,wyz,xxz,xyz,所以就会匹配出xyz
const reg1 = /[wx][xy][z]/;
const str1 = 'wxyz';
console.log(str1.match(reg1));
// [xyz]
5、[] 区间 用 - 代表区间;匹配的顺序要按照ASCII值。比如[a-Z]就是不对的 a > Z,因为超出了ASCII值的范围
const reg = /[0-9]/;
/*
符合的区间
[0-9A-Za-z]
[0-9A-z]
[0-Z]
[0-z]
[^0] 第一位是0
^[0] 第一位非0
*/
6、元符号 一个元符号代表一位
/*
\w === [0-9A-z_]
\W === [^\w]
\d === [0-9]
\D === [^0-9]
\s === [\r\n\t\r\f] // 转义符号
\S === [^\s]
\b 单词边界 字符串开头与结尾,完整单词左右,单个字符(不包括符号)左右位置
\B 非单词边界 [^\B]
. 除了回车和换行的所有字符
*/
const reg = /\wab/g;
const str = '0abdf';
console.log(str.match(reg)); // ['0ab']
7、转义符号
\t 制表符 tab键
\n 换行符
\r 回车符
\v 垂直换行
\f 换页符
进阶一
量词
1、+ 一次到正无穷 {1, 正无穷}
const reg = /\w+/g;
const str = 'abcgsjfhas';
console.log(str.match(reg)); // [ 'abcgsjfhas' ]
const str1 = 'abcgs+jfhas';
console.log(str1.match(reg)); // [ 'abcgs', 'jfhas' ]
2、* 0次到正无穷 {0, 正无穷} 比 + 多匹配出一个空
const reg = /\w*/g;
const str = 'abcgsjfhas2345';
console.log(str.match(reg)); // [ 'abcgsjfhas2345', '' ]
// 之所以会有一个空字符串 是因为最后没有匹配到是0 所以返回空字符串
3、? 0到1 {0,1}
// 因为是 0个或1个 所以就是一个字符一个字符匹配。
const reg = /\w?/g;
const str = 'abcgsjfhas2345';
console.log(str.match(reg)); // ['a', 'b', 'c', 'g', 's','j', 'f', 'h', 'a', 's', '2', '3', '4', '5', '']
4、{x,y} x到y个 之间不能有空格
// 匹配1个或两个 因为贪婪模式所以默认按最多显示数匹配
const reg = /\w{1,2}/g;
const str = 'abcgsjfhas2345';
console.log(str.match(reg)); // [ 'ab', 'cg', 'sj', 'fh', 'as', '23', '45']
5、^n 匹配任何以n开头的字符串
// 匹配以ab开头字符串
const reg = /^ab/g;
const str = 'abcgsjf+abhas2345';
console.log(str.match(reg)); // [ 'ab' ]
6、n$ 匹配任何以n结尾的字符串
// 匹配以45结尾字符串
const reg = /45$/g;
const str = 'abcgsjf+abhas2345';
console.log(str.match(reg)); // [ '45' ]
7、问题:检查字符串是否以abcd开头以abcd结尾
// 中间要匹配任意字符
const reg = /^abcd.*abcd$/g;
const str = 'abcd1234124abcd';
console.log(str.match(reg)); // [ 'abcd1234124abcd' ]
// 任意字符 可以用 [\s\S]
// ^abcd& 这样写是不对的(原因 结束是开头的字符串 但是两者是不同的)
8、正向预查、不捕获分组
查找一个字符串 后面紧跟什么的字符串, 注意要通过()括起来当做表达式使用
- ?=n 匹配任何其后紧跟着指定字符串n的字符串
- ?!n 匹配任何其后不紧跟着指定字符串n的字符串
- ?:n 不捕获其后字符
// 查找ab后紧跟c的ab字符串
const reg = /ab(?=c)/g;
const str = 'abcdabcd';
console.log(str.match(reg)); // ['ab', 'ab']
// 不捕获分组
const reg = /(?:a)(b)(c)/;
const str = 'abcabc';
console.log(str.match(reg));
/**
* ['abc', 'b', 'c', index: 0, input: 'abcabc', groups: undefined] 就是子表达式不显示出来
* 正常下 ['abc', 'a', 'b', 'c', index: 0, input: 'abcabc', groups: undefined] 正常 a 是捕获出来的
*/
9、子表达式()与反向引用 \1
子表达式:有记忆性的,与反向引用一起用时, 每一次反向引用, 引用记忆的那个字符。
反向引用: \1 反向引用第几个子表达式(从1开始), 要引用几次就写几个\1
// 匹配 xxxx
const reg = /(\w)\1\1\1/g;
const str = 'bbcccccaasssffff';
console.log(str.match(reg)); // ['cccc', 'ffff']
// 匹配 xxyy
const reg = /(\w)\1([A-z])\2/g;
const str = 'bbcccccaasssffff';
console.log(str.match(reg)); // ['bbcc', 'ccaa', 'ssff']
10、对象属性与方法
// 属性
const regObj = /(\w)\1([A-z])\2/g;
// 是否设置修饰符属性
regObj.global;
regObj.ignoreCase;
regObj.multiline;
// 表达式体
regObj.source;
// 下标位置 可通过lastIndex 修改下标位置 并影响exec中的index
// 但是 lastIndex 修改的不是exec匹配的,exec会寻找与当前下标相近的可匹配的下标。
regObj.lastIndex;
// 方法
// 返回TRUE or FALSE
regObj.test(String);
// 返回的是类数组 但是可以连续匹配 是一轮一轮匹配 循环匹配的 直到匹配到null
// lastIndex 与 exec中index是吻合的
// exec 匹配反向引用子表示,会把子表示每次引用的字是什么返回出来
regObj.exec();
const regExec = /123/g;
const strExec = '123123324122';
console.log(regExec.exec(strExec));
// ['匹配出来的数据, '开始匹配的下标', '被匹配的字符串']
// [ '123', index: 0, input: '123123324122', groups: undefined ]
const regExec = /(\w)\1(\w)\2/g;
const strExec = 'bbcccccfffcccsss';
console.log(regExec.exec(strExec));
// ['匹配出来的数据, '子表达式1数据', '子表达式2数据', '开始匹配的下标', '被匹配的字符串']
// ['bbcc', 'b', 'c', index: 0, input: 'bbcccccfffcccsss', groups: undefined]
进阶二
replace
参数1:字符串或正则
参数2:字符串或回调函数
// JSScript 将Script 去除
const repStr = 'JSScript';
const result = repStr.replace(/Script/g, '');
console.log(result); // JS
将格式为 xxyy 翻转为 yyxx
// 字符串格式
const repStr = 'aabbccddffgg';
const repReg = /(\w)\1(\w)\2/g;
// $1 $2 分别代表子表达式1和2
const result = repStr.replace(repReg, '$2$2$1$1');
console.log(result); // 'bbaaddccggff'
// 回调函数
const result1 = repStr1.replace(repReg, ($, $1, $2) => {
// $ 为被匹配的数据 aabb
// $1 为第一个子表达式 a
// $2 为第二个子表达式 b
// console.log($, $1, $2);
return $2 + $2 + $1 + $1;
});
console.log(result1); // 'bbaaddccggff'
js-script 改为 jsScript
// 字符串
const repStr = 'js-script';
// 子表达式为 -s
const result = repStr.replace(/(\-\w)/g, 'S');
console.log(result); // 'jsScript'
// 回调函数
// 子表达式为 s
const result1 = repStr2.replace(/-(\w)/g, ($, $1) => {
return $1.toUpperCase();
});
console.log(result1); // 'jsScript'
xxyyzz -> XxYyZz
const repStr = 'xxyyzz';
const result7 = repStr4.replace(/(\w)\1(\w)\2(\w)\3/g, ($, $1, $2, $3) => {
return $1.toUpperCase() + $1 + $2.toUpperCase() + $2 + $3.toUpperCase() + $3;
});
aabbcc -> a$b$c$ 不能用function
const repStr = 'aabbcc';
// $是特定 如果想用$ 连写两个$$
const result = repStr.replace(/(\w)\1(\w)\2(\w)\3/g, '$1$$$2$$$3$$');
console.log(result); // a$b$c$
aa\bb\cc 匹配 \
const repStr = 'aa\\bb\\cc';
// \是特殊符号要用转义符,
const repReg = /(\\)+/g;
console.log(repStr.match(repReg)); // ['\\', '\\']
aabbcc -> abc 等个字符串去重
const repStr = 'aabbcc';
const result = repStr.replace(/(\w)\1(\w)\2(\w)\3/g, '$1$2$3');
console.log(result); // abc
aaabbbbbcccccc -> abc 不等个字符串去重
const repStr = 'aaabbbbbcccccc';
// * 是对于反向引用 \1 来说的
const result = repStr.replace(/(\w)\1*/g, '$1');
console.log(result); // abc
数字千分位逗号隔开
// 10000000000 变成 1,000,000,000
// 1、从左往右截取 缺点:如果最后不是3位时 100,000,000,00 是不对的
const repStr = '1000000000';
// \B 非单词边界 每3位的最后为非单词边界,但是整个字符串的开始与结束未单词边界,所以最后正好为3位时也不会加上,
const result = repStr.replace(/(\d{3}\B)/g, ($, $1) => {
return $1 + ','
});
// 2、推荐 从右往左截取
/**
* 用正向预查 以3位3位为结尾的 每次匹配出来的都是空 前面是个空
* 每次都是以空位开始,后面紧跟3个数字位结尾,还得是个非单词边界
*/
const repReg = /(?=(\B)(\d{3})+$)/g;
const result1 = repStr.replace(repReg, ','); // 1,000,000,000
// 方案2中正则可用于诸如类似与每几位字符串之间添加字符的案例。
// 如 abaskfaksdfajfhadjbfadf 每4位之间加一个-
const repStr2 = 'abaskfaksdfajfhadjbfadf';
const result2 = repStr2.replace(/(?=(\B)(\w{4})+$)/g, '-');
模板匹配 {{}}
const obj = {
name: '李华',
age: 34
};
const repStr = 'My name is {{name}}, I am age is {{age}}。';
// .*? 必须加括号变成子表达式,这样才能在replace获取到
const repReg = /\{\{(.*?)\}\}/g;
const result = repStr14.replace(repReg, ($, $1) => {
return obj[$1];
});
console.log(result);
// My name is 李华, I am age is 34。