正则表达式大法

247 阅读2分钟
RegExp 正则表达式
常用的元字符

量词元字符

  • *****零到多个

  • **+**一个到多个

  • **?**零次或一次

  • {n} 匹配n次

  • {n,} 出现n到多次

  • {n,m} 出现n到m次

    特殊元字符

  • \ 转义元字符:单个或者组合在一起代表特殊含义

  • . 除了\n(换行符)以外的任意字符

  • ^ 以哪一个元字符作为开始

  • $ 以哪一个元字符作为结束

  • \n 换行符

  • \d 0~9之间的数字

  • \D 匹配一个非数字字符.

  • \w 数字,字母,下划线 中的任意一个字符

  • \s 一个空白字符

  • \t 一个制表符

  • x|y x或y中的一个字符 一般不单独使用 配合()使用

  • [xyz] x或者y或者z中的一个字符

  • [^xyz] 除了x,y , z中的任意一个字符

  • [a-z] 指定 a-z这个范围中的任意字符 [0-9a-zA-Z]===\w

  • [^a-z] 除了a到z

  • () 正则中的分组符号

    • 优先计算
    • 分组捕获
    • 分组引用
  • (?:) 只匹配不捕获

  • (?=) 正向预查 后面是个条件但是不匹配也不捕获 exp1(?=exp2) exp1后边是exp2就匹配

  • (?!) 反向预查 后面是个条件但是不匹配也不捕获 exp1(?!exp2) exp1后边不是exp2就匹配

    正则表达式常用修饰符

  • i 忽略大小写匹配

  • m 可以进行多行匹配

  • g 可以进行全局匹配

实现正则捕获的办法
  • 正则RegExp.prototype上的方法
    • exec
    • test
  • 字符串String.prototype上支持正则表达式的处理方法
    • replace
    • match
    • split
看下面例题
let reg = /\d$/,
    str1 = "yzl2021",
    str2 = "2021yzl",
    str3 = "yz2021l";

console.log(reg.test(str1));	//->true
console.log(reg.test(str2));	//->false
console.log(reg.test(str3));	//->false

//分析:$代表结尾的意思  说明以数字结尾就算匹配上
let reg = /^2.3$/;
console.log(reg.test(2.3));		//->true
console.log(reg.test('2@3'));	//->true
console.log(reg.test(23));		//->false

//分析: .可以代表除了\n的元字符
let reg = /^2\.3$/;
console.log(reg.test(2.3));		//->true
console.log(reg.test('2@3'));	//->false

//分析: \把.给转义了所以2@3匹配不上
let str = "\\d",
    reg = /^\d$/;
console.log(reg.test(str));		//->false
	reg = /^\\d$/;
console.log(reg.test(str));		//->true

正则表达式验证是否为有效数字

let reg = /^[+-]?\d+(\.\d)?$/;
console.log(reg.test('10')); 	//=>true
console.log(reg.test('10.1')); 	//=>true
console.log(reg.test('-10.1')); //=>true

验证有效密码

//密码为字母数字下划线	6-10位数字
let reg = /^\w{6,10}$/;
console.log(reg.test("qwerty"))	//=>true
console.log(reg.test("qwert"))	//=>false

验证真实姓名

/*
	1.中文汉字的编码[\u4E00-\u9FA5]
	2.名字长度2-10
*/
let reg = /^[\u4e00-\u9fa6]{2,10}(\·[\u4e00-\u9fa6]{2,10})?$/;
console.log(reg.test('切格瓦拉'));

分组捕获

/*
	1.使用场景 比如:2021 我要使第一位和第三位相同
	2.()表示捕获	\1表示与第一个捕获相同 \2表示与第		二个捕获相同
*/
let reg = /^(\d)\d\1\d$/;
console.log(reg.test('2021'));	//=>true
console.log(reg.test('2031'))	//=>false

正则捕获的懒惰性

/*
	1.exec方法只能获取一个捕获
*/
let reg = /[\u4e00-\u9fa0]/g;
console.log(reg.exec("我2在0学2习1"));	
/*
	[0: "我"
	groups: undefined
	index: 0
	input: "我2在0学2习1"
	length: 1]
*/


/*	
	2然后我们又想获取所有匹配上的数据我们就可以自己封装一个		Allexec方法
*/

let reg = /[\u4e00-\u9fa0]/g;
	// console.log(reg.exec("我2在0学2习1"));
	RegExp.prototype.Allexec = function (str){
		// 大捕获
		let bigarr = [],
		// 小捕获
			smallarr = [];
		if(!this.global){
			bigarr.push(this.exec(str)[0]);
			smallarr.push(this.exec(str)[0]);
			return {
				bigarr,
				smallarr
			}
		}else{
			let data = this.exec(str);
			while(data){
				bigarr.push(data[0]);
				smallarr.push(data[0]);
					data = this.exec(str);
				}
			return {
				bigarr,
				smallarr
			}
		}
	}
console.log(reg.Allexec("我2在0学2习1"));
/*
	bigarr: (4) ["我", "在", "学", "习"]
	smallarr: (4) ["我", "在", "学", "习"]
*/

/*
	3.我们也可以使用字符串中的match方法来批量捕获
*/
let str = "我2在0学2习1",
	reg = /[\u4e00-\u9fa0]/g;
console.log(str.match(reg));
//=>	["我", "在", "学", "习"]

正则表达式的贪婪性

/*
	1.正则表达式具有贪婪性,默认捕获最长的.
	2.在量词后面加上?就可以取消贪婪性
	3.取消贪婪性就匹配最短的
		比如:+是匹配1到多个+?就匹配1个字符为一组
			 *是匹配0到多个*?就匹配0个字符为一组
			 {2,4}	就匹配2个字符为一组
*/

let reg = /\d+/g;
console.log('2021'.match(reg));
//=> ["2021"]

let reg = /\d+?/g;
console.log('2021'.match(reg));
//=> ["2", "0", "2", "1"]

let reg = /\d*?/g;
console.log('2021'.match(reg));
//=> ["", "", "", ""]

let reg = /\d{2,}?/g;
console.log('2021'.match(reg));
//=> ["20", "21"]
其他正则的捕获方法

test也能捕获(本意是匹配)

let reg = /(\d+)/g;
console.log(reg.test("2我0要努2力1")); //=>true
console.log(RegExp.$1);				  //=>2

console.log(reg.test("2我0要努2力1")); //=>true
console.log(RegExp.$1);				  //=>0
replace 方法捕获
let reg = /sjl/;
let str = "sjl是个机灵鬼";
str = str.replace("sjl","yzl");
console.log(str);

/*
	下面看一个不用正则的弊端
*/
let reg = /sjl/g;
let str = "sjl是个机灵鬼sjl";
str = str.replace("sjl","sjl儿子").replace("sjl","sjl儿子");
console.log(str); //=>sjl儿子儿子是个机灵鬼sjl

/*
	replace 正则表达式的匹配规则
	1.首先拿REG和Str进行匹配捕获,能匹配几次就会把传递的函数执行几次(而且是匹配一次就执行一次)
	2.不仅执行方法,而且replace 还给方法传递了实参(和exec捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息...)
	3.replace 回调函数返回的return是直接替换大捕获
*/
//第一种写法
let time = "2021/4/26",
	reg = /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/;
time = time.replace(reg,function(content,$1,$2,$3){
    return $1+'-'+$2+'-'+$3
})
console.log(time)	//2021-4-26

//第二种写法
let time = "2021/4/26",
	reg = /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/;
time = time.replace(reg,function(...arg){
    //数组的结构赋值 如果不要大捕获content就 let [_,$1,$2,$3] = arg;或者let 		[,$1,$2,$3] = arg 占位;
    let [content,$1,$2,$3] = arg;
    return $1+'-'+$2+'-'+$3
})
console.log(time);	//2021-4-26	

单词首字母大写

let str = "hellow world",
    reg = /\b([a-z])[a-z]*\b/g;
str = str.replace(reg,function(...arg){
    let [content,$1,$2,$3] = arg;
    return $1.toUpperCase()+content.substring(1);
})
console.log(str)		//=>Hellow World

统计一个字符串中出现最多的字母

/*
	方法一:对象去重
*/
let str = "kknuwesadqwkkgxf",
	obj = {},
	max = 0,
    //出现最多次的字符串数组
    code = [];
//调用数组的forEach方法。并且用call更改this指向str
[].forEach.call(str,(v,i)=>{
    //如果不是undefined 就说明字符已经出现过了所以就++
	//也可以这样写 obj.hasOwnProperty(v) obj对象中是否出现过v属性
    if(obj[v]!=undefined){
        obj[v]++;
    }else{
        obj[v] = 1;
    }		
})
//找到最大值
for (const key in obj) {
    if(obj[key]>max){
        max = obj[key];
    }
}
//压入数组
for (const key in obj) {
    if(obj[key]===max){
        code.push(key);
    }
}
console.log(code);			//=>["k"]

/*
	方法二:正则表达式匹配的方法
*/
let str = "kknuwesadqwppppkkgxf",
    reg =/([a-zA-Z])\1+/g,
    newdata = [],
    maxlen = 0,
    code = [];
/*
	1.str.split("") 字符串变成数组
	2.sort((a,b)=>a.localeCompare(b)) 数组排序(字符串比较用localeCompare)
	3.join("") 重新拼接成字符串
*/
str = str.split("").sort((a,b)=>a.localeCompare(b)).join(""); //=>"adefgkkkknppppqsuwwx"
newdata = str.match(reg).sort((a,b)=>b.length-a.length); //=>["kkkk", "pppp", "ww"]
maxlen = newdata[0].length;
for (const v of newdata) {
    if(v.length === maxlen) code.push(v.substr(0,1))
}
console.log(code);	//=>["k", "p"]

/*
	正则方法二
	思路:求字符串长度(str)然后用正则去匹配 let reg = new RegExp("([a-zA-Z])\\1{"+(i-1)+"}",'g');
	     如果能匹配上就说明已经匹配到了最长的字符串 因为i是str.length--下来的
*/
let str = "kknuwesadqwppppkkgxf",
    strlen = str.length,
    flat = false,
    codearr = [];
for(let i =strlen;i>0;i--){
    let reg = new RegExp("([a-zA-Z])\\1{"+(i-1)+"}",'g');
   str.split('').sort((a,b)=>a.localeCompare(b)).join("").replace(reg,function(...arg){
        let [_,$1] = arg;
        codearr.push($1);
        flat = true;
    })
    if(flat) break;
}
console.log(codearr)

/*
	正则方法三
	思路:1.每次匹配一个字符
		 2.然后用正则匹配把str中相同的字符全删除
		 3.比较删除之前和删除之后str的字符在differ存储差值
		 4.if(differ>=max) 如果差值大于max就存对象中并存储max 因为存obj中的值是假设最大
		 5.forin 遍历obj 如果 max === obj[key]就是出现最多的字符
*/
let str = "kknuwesadqwppppkkgxf",
    letter = '',
    codearr = [],
    max = 0,
    oldlen,
    newlen,
    obj = {},
    differ=-1;
//只要str没有变成空
while(str!==''){
    //存储之前未删除的str长度
    oldlen = str.length;
    letter = str[0];
    let reg = new RegExp(letter,'g');
    str = str.replace(reg,"");
    newlen = str.length;
    differ = oldlen - newlen;
    if(differ>=max){
        obj[letter] = differ;
        max = differ;
    }
}
for (const key in obj) {
    if(max === obj[key]){
       codearr.push(key);
    }
}
console.log(codearr)

使用正则表达式 自定义格式化时间格式

/*
 *	思路:1.首先第一步,有一个系统的时间 let time = '2021/4/27 17:01';
 	     2.使用let timearr = time.match(reg) 把系统时间都给捕获出来
 	     3.使用template.replace(reg,function(){})使用timearr数组数据替换模板
 */	
//系统时间
let time = '2021/4/27 17:01';
//在单例模式中修改string的原型
~function(){
   	//如果没有传格式就默认"{0}年{1}月{2}日 {3}时{4}分{5}秒"
    function formatTime(template = "{0}年{1}月{2}日 {3}时{4}分{5}秒"){
        //把系统时间用正则分离出来
        let timeArr = this.match(/\d+/g);
        //替换template中的{index}
        return template.replace(/\{(\d+)\}/g,(...[,$1]) => {
            //如果$1大于数组长度就"00"
            let timedata = timeArr[$1] || "00"
            //如果只有单个数字就拼接'0'
            timedata.length<2?timedata='0'+timedata:null;
            return timedata;
        })
    }
	//这个就很高级要学会
    ["formatTime"].forEach((v,i) => {
        // eval(v) ==>返回一个函数		v返回一个字符串
        String.prototype[v] = eval(v);
    })
}()

console.log(time.formatTime("{0}年{1}月{2}日"))

正则实现queryURLParams 把GET参数提取到obj对象中

//正则实现queryURLParams
~function(){
    function queryURLParams(){
        let obj = {};
        // 获得参数params
        this.replace(/([^%=?#]+)=([^%=?#]+)/,(...[_,$1,$2]) => {
            obj[$1] = $2;
            return ;
        })
        //获得锚点
        let reg = /#([^%=?#]+)/;
        obj["HASH"] = reg.exec(this)[1]
        return obj;
    }

    ["queryURLParams"].forEach((v,i) => {
        // eval(v) ==>返回一个函数		v返回一个字符串
        String.prototype[v] = eval(v);
    })
}()

let str = "https://www.mosoteach.cn/web/index.php?c=clazzcourse#kkk";
console.log(str.queryURLParams());

正则表达式实现千分符分割数据

//正则表达式实现千分符
let str = '12321321321313';
~function(){
    function micrometer(){
        /*
        	1.开头可以是(\d{1,3})1-3个数字
        	2.(\d{1,3})(?=(\d{3})+$)
        		+(\d{1,3})后面必须是3个数字为一组
        		+$标识结尾也必是是3个数字
        */
        let reg = /(\d{1,3})(?=(\d{3})+$)/g;
        return this.replace(reg,(...[content]) => {
            console.log(content);
            return content+',';
        });
    }

    ['micrometer'].forEach((v,i) => {
        String.prototype[v] = eval(v);
    })
}()

console.log(str.micrometer());

传统方法实现数字千分符

//字符串分割的方法实现千分符
/*
	思路:1.字符串转变为数组
		 2.数组取反
		 3.for循环拼接,
		 4.数组取反变为字符串输出
*/
let str = '123213321313';
~function(){
    function micrometer(){
        let str = this.split('').reverse().join("");
        for (let index = 2; index < str.length-1; index=index+4) {
            let prestr = str.substring(0,index+1)+',',
                nextstr = str.substring(index+1,str.length);
            str = prestr + nextstr;
        }
        return str.split("").reverse().join("");
    }

    ['micrometer'].forEach((v,i) => {
        String.prototype[v] = eval(v);
    })
}()

console.log(str.micrometer());