reg总结

299 阅读8分钟

什么是正则表达式

  • 是一个规则,只能处理字符串,
  • 验证字符串是否符合某个规则(test)
  • 内容捕获到(exec / match...) ,对字符串中符合规则的内容进行捕获

正则创建方式

  1. 字面量创建方式
let reg1 = /\d+/g;
  1. 构造函数模式创建
let reg2 = new RegExp("\\d+","g");
  1. 包含变量的创建方式
    两个斜杠中间包起来的都是元字符(如果正则中包含某个变量的值则不能使用字面量方式创建)
    这种情况只能使用构造函数方式(因为它传递的是字符串,才能用字符串拼接)
let type = "kendeji",
reg = new RegExp("^@"+type+"@$")
console.log(reg.test('@kendeji@'))  

4.构造函数因为传递的是字符串,\需要写两个才代表斜杠

let reg = /\d+/g;
reg = new RegExp("\\d+","g");

常用正则元字符&修饰符

常用的元字符

1.量词元字:设置出现的次数

元字符含义
*零到多次
+一到多次
零次或者一次
{n}出现n次
{n,}出现n到多次
{n,m}出现n到m次

2.特殊元字符:单个或者组合在一起代表特殊的含义

元字符含义
\转义字符(普通->特殊->普通)
.除\n(换行符)以外的任意字符|
以哪一个元字符作为开始
$以哪一个元字符作为结束
\n换行符
\d0~9之间的一个数字
\D非0~9之间的一个数字 (大写和小写的意思是相反的)
\w数字、字母、下划线中的任意一个字符
\s一个空白字符(包含空格、制表符、换页符等)
\t一个制表符(一个TAB键:四个空格)
\b匹配一个单词的边界
x竖线yx或者y中的一个字符
[xyz]x或者y或者z中的一个字符
[^xy]除了x/y以外的任意字符
[xyz]x或者y或者z中的一个字符
[^xy]除了x/y以外的任意字符
[a-z]指定a-z这个范围中的任意字符 [0-9a-zA-Z_]===\w
[^a-z]上一个的取反“非”
()正则中的分组符号
(?:)只匹配不捕获
(?=)正向预查
(?!)负向预查

3.普通元字符:代表本身含义的

元字符含义
/kendeji/此正则匹配的就是"kendeji"

常用的修饰符

修饰字符含义
i忽略单词大小写匹配
m可以进行多行匹配
g全局匹配

元字符详细解析

^ $

let reg = /^\d/;
console.log(reg.test("kendeji")); //=>false
console.log(reg.test("2019kendeji"));//=>true
console.log(reg.test("kendeji2019"));//=>false
let reg = /\d$/;
console.log(reg.test("kendeji")); //=>false
console.log(reg.test("2019kendeji"));//=>false
console.log(reg.test("kendeji2019"));//=>true
  • ^或$两个都不加:字符串中包含符合规则的内容即可
let reg1 = /\d+/;
  • ^或$两个都加:字符串只能是和规则一致的内容
let reg2 = /^\d+$/;

举个例子:验证手机号码(11位,第一个数字是1即可)

let reg = /^1\d{10}$/;

\

  • . 不是小数点,是除\n外的任意字符,基于转义字符,让其只能代表小数点
let reg = /^5.3$/;
console.log(reg.test("5.3"));//=>true
console.log(reg.test("5@3"));//=>true
console.log(reg.test("53"));//=>false


reg = /^5\.3$/;
console.log(reg.test("5.3"));//=>true
console.log(reg.test("5@3"));//=>false
  • 把特殊符号转换为普通的 匹配'\d'字符串
let str = "\\d";
//字符串\\d 表示\d
reg = /^\d$/; 
//=>\d代表0-9的数字
console.log(reg.test(str)); //=>false
reg = /^\\d$/; 
//=>把特殊符号转换为普通的
console.log(reg.test(str)); //=>true

x|y

x|y会有优先级问题,
解决:小括号进行分组,改变优先级

let reg = /^13|29$/;
console.log(reg.test("13")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("139")); //=>true

reg = /^(13|29)$/;
//只能是13或者29中的一个
console.log(reg.test("13")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("139")); //=>false

[]

  • 中括号中出现的字符一般都代表本身的含义
let reg = /^[@+]$/;
/^[]/
console.log(reg.test("@")); //=>true
console.log(reg.test("+")); //=>true
console.log(reg.test("@@")); //=>false
console.log(reg.test("@+")); //=>false

reg = /^[\d]$/; //=>\d在中括号中还是0-9
console.log(reg.test("d"));//=>false
console.log(reg.test("\\"));//=>false
console.log(reg.test("9"));//=>true
  • 中括号中不存在多位数
reg = /^[13]$/;
console.log(reg.test("1")); //=>true
console.log(reg.test("3")); //=>true
console.log(reg.test("13")); //=>false

reg = /^[13-55]$/; //=>1或者3-5或者5
console.log(reg.test("1"));//=>true
console.log(reg.test("3"));//=>true
console.log(reg.test("13"));//=>false
console.log(reg.test("55"));//=>false

常用的正则表达式

验证是否为有效数字

分析:

  1. 可能出现 + - 号,也可能不出现
  2. 一位0-9都可以,多位首位不能是0
  3. 小数部分可能有可能没有,一旦有后面必须有小数点+数字
//1.[+-]?
//2.(\d|([1-9]\d+))
//3.(\.\d+)?

let reg = /^[+-]?(\d|[1-9]\d+)(\.\d+)?$/;

验证密码

  1. 数字、字母、下划线 2.6~16位
let reg = /^\w{6,16}$/

验证邮箱

 let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;

分析:
比如:
huang-mei-mei
huang-mei.mei
hang.mei.mei

let reg = /^\w+((-\w+)|(\.\w+))*$/

@后面:数字、字母 (1-多位)

@[A-Za-z0-9]+

对@后面名字的补充 多域名 .com.cn
企业邮箱 hkl@huang-meimei-home.com

((\.|-)[A-Za-z0-9]+)*

这个匹配的是最后的域名(.com/.cn/.org/.edu/.net...)

\.[A-Za-z0-9]+

正则的捕获

实现正则捕获的办法

正则RegExp.prototype上的方法字符串String.prototype上支持正则表达式处理的方法
execreplace
testmatch
splite

exec正则捕获

let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/;
console.log(reg.exec(str))

  "2019"              //本次捕获到的内容
  groupsundefined   //对应小分组本次单独捕获的内容
  index7            //当前捕获内容在字符串中的起始索引
  input"kendeji2019maidanglao2020bishengke2021"     //原始字符串
  length1

问题: 结果永远都是第一个匹配到的,其余的捕获不到
解决办法:  全局修饰符g

match正则捕获

let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/g;
console.log(str.match(reg))  

// ['2019', '2020', '2021']  

正则捕获的懒惰性

正则捕获的懒惰性,默认只捕获第一个

问题: 每一次执行exec,结果永远都是第一个匹配到的,其余的捕获不到

原因: 默认情况下lastIndex的值不会被修改,每一次都是从字符串开始位置查找

解决办法: 全局修饰符g

设置全局匹配修饰符g后,第一次匹配完,lastIndex会自己修改。当全部捕获后,再次捕获的结果是 null,lastIndex变为初始值零,再次捕获又从第一个开始了

let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/g;
console.log(reg.exec(str))
//['2019', index: 7, input: 'kendeji2019maidanglao2020bishengke2021', groups: undefined]
console.log(reg.exec(str))
//['2020', index: 21, input: 'kendeji2019maidanglao2020bishengke2021', groups: undefined]
console.log(reg.exec(str))
//['2021', index: 34, input: 'kendeji2019maidanglao2020bishengke2021', groups: undefined]

exec问题:
每执行一次,lastIndex改变一次,要多次执行

解决:
1.字符串中的MATCH方法,可以在执行一次的情况下,捕获到所有匹配的数据(前提:正则也得设置G才可以)

let str = "kendeji2019maidanglao2020bishengke2021";
let reg = /\d+/g;
console.log(str.match(reg))  

// ['2019', '2020', '2021']  
默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的  
  1. 编写一个方法execAll,执行一次可以把所有匹配的结果捕获到(前提正则一定要设置全局修饰符g)
~ function () {
    function execAll(str = "") {
        //=>str:要匹配的字符串
        //=>this:RegExp的实例(当前操作的正则)
        //=>进来后的第一件事,是验证当前正则是否设置了G,不设置则不能在进行循环捕获了,否则会导致死循环
        if (!this.global) return this.exec(str);
        //=>ARY存储最后所有捕获的信息  RES存储每一次捕获的内容(数组)
        let ary = [],
            res = this.exec(str);
        while (res) {
            //=>把每一次捕获的内容RES[0]存放到数组中
            ary.push(res[0]);
            //=>只要捕获的内容不为NULL,则继续捕获下去
            res = this.exec(str);
        }
        return ary.length === 0 ? null : ary;
    }
    RegExp.prototype.execAll = execAll;
}();

let reg = /\d+/g;
console.log(reg.execAll("kendeji2019maidanglao2020bishengke2021"));

正则捕获的贪婪性

let str = "kendeji2015@1238bihsengke";
let reg = /\d+/g
console.log(str.match(reg))
// ['2015', '1238']

在量词元字符后面设置?,取消捕获时候的贪婪性(按照正则匹配的最短结果来获取)

let str = "kendeji2015@1238bihsengke";
let reg = /\d+?/g;
console.log(str.match(reg))
//['2', '0', '1', '5', '1', '2', '3', '8']

正则的分组捕获

let str = '135368199812320168'
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d)$/
console.log(reg.exec(str));  
console.log(str.match(reg))
//['135368199812320168', '135368', '1998', '12', '32', '6', '8', index: 0, input: '135368199812320168', groups: undefined]  
  • 捕获的时候不需要单独捕获,添加?:
let str = '135368199812320168'
let reg = /^(\d{6})(?:\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d)$/
console.log(reg.exec(str));  
console.log(str.match(reg))

// ['135368199812320168', '135368', '12', '32', '6', index: 0, input: '135368199812320168', groups: undefined]
  • match分组捕获的问题
    需求: 字符串‘{0}年{1}月{2}日’ ,要匹配{0} 和0
let reg = /\{(\d+)\}/
let str = "{0}年{1}月{2}日"
console.log(reg.exec(str));
console.log(str.match(reg))
//['{0}', '0', index: 0, input: '{0}年{1}月{2}日', groups: undefined]

问题: 正则不设置g只匹配一次,exec和match结果一样,设置g后,match小分组匹配的信息无法获取 解决:

let reg = /\{(\d+)\}/g;
let aryBig=[],
    arySmall=[],
    res=reg.exec(str);
while(res){
    let [big,small]=res;
    aryBig.push(big);
    arySmall.push(small);
    res=reg.exec(str);
}
console.log(aryBig,arySmall); //=>["{0}", "{1}", "{2}"] ["0", "1", "2"]

分组引用

分组引用就是通过“\数字”让其代表和对应分组出现一模一样的内容

let str = "keep"; 
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; 
console.log(reg.test("beek")); //=>true
console.log(reg.test("deep")); //=>true
console.log(reg.test("hope")); //=>false

总结:问号在正则中的五大作用

  1. 问号左边是非量词元字符:本身代表量词元字符,出现零到一次
let reg = /\d+/

2.问号左边是量词元字符:取消捕获时候的贪婪性

let str = "kendeji2015@1238bihsengke"; 
let reg = /\d+?/g;
console.log(str.match(reg)) //['2', '0', '1', '5', '1', '2', '3', '8']
  1. (?:) 只匹配不捕获
let str = '135368199812320168'
let reg = /^(\d{6})(?:\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d)$/
console.log(reg.exec(str));  
console.log(str.match(reg))

// ['135368199812320168', '135368', '12', '32', '6', index: 0, input: '135368199812320168', groups: undefined]

  1. (?=) 正向预查
  2. (?!) 负向预查

总结:小括号在正则中的三大作用

  1. 改变优先级
    匹配13或29

错误写法:

let reg = /^13|29$/; 
console.log(reg.test("13")); //=>true console.log(reg.test("29")); //=>true console.log(reg.test("139")); //=>true 
console.log(reg.test("132")); //=>true 

用小括号改变优先级

reg = /^(13|29)$/; //只能是13或者29中的一个 
console.log(reg.test("13")); //=>true
console.log(reg.test("29")); //=>true 
console.log(reg.test("139")); //=>false

2.分组捕获

let str = '135368199812320168'
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d)$/
console.log(reg.exec(str));  
console.log(str.match(reg))
//['135368199812320168', '135368', '1998', '12', '32', '6', '8', index: 0, input: '135368199812320168', groups: undefined]  
  1. 分组引用
let str = "keep"; 
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; 
console.log(reg.test("beek")); //=>true
console.log(reg.test("deep")); //=>true
console.log(reg.test("hope")); //=>false

replace字符串中实现替换

  • 'kendejig@222|kendeji@2222',
    把kendejig替换为KFC。
let str = 'kendejig@222|kendeji@2222'
   str = str.replace(/kendeji/g,"KFC");
   console.log(str);  
   //KFCg@222|KFC@2222

练习

时间字符串处理

"2020-03-18" 变为 2020年03月18日

方法一:

let str = '2020-03-18'  
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str = str.replace(reg,"$1年$2月$3日")
console.log(str)
//2020年03月18日

方法二: [str].replace([reg],[function])
用str与reg匹配,匹配一次function就执行一次 , 在函数中我们返回的是什么,就把当前大正则匹配的内容替换成什么

let str = '2020-03-18'  
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
str = str.replace(reg,(big,$1,$2,$3)=>{
    //=>这里的$1~$3是我们自己设置的变量
    //=> big 大正则匹配的内容
    
    console.log(big,$1,$2,$3);
});
   str = str.replace(reg,(...arg)=>{
    let [,$1,$2,$3]=arg;
    $2.length<2?$2="0"+$2:null;
    $3.length<2?$3="0"+$3:null;
    return $1+"年"+$2+"月"+$3+"日";
});

时间字符串的格式化处理

   let str = '2016/05/12',
       templete = '{0}年{1}月{2}日 {3}时{4}分{5}秒'
   function formatTime() {
		let timeAry = str.match(/\d+/g);
		return templete.replace(/\{(\d+)\}/g, (...[, $1]) => {
			let time = timeAry[$1] || "00";
			return time.length < 2 ? "0" + time : time;
		});
	}
     
    console.log(formatTime(templete,str))//2016年05月12日 00时00分00秒
     templete = '{0}-{1}-{2} {3}:{4}:{5}'
     console.log(formatTime(templete,str))  //2016-05-12 00:00:00
     

单词首字母大写

let str = "you can you up a"
let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
//=>函数被执行了五次,每一次都把正则匹配信息传递给函数
//=>每一次ARG:["you","y"] ["can","c"] ["you","y"]...
 str = str.replace(reg,(...arg)=>{
    let [content,$1]=arg;
    $1=$1.toUpperCase();
    content=content.substring(1);
    return $1+content;
});
console.log(str)
//You Can You Up A

queryURLParams :获取URL地址问号和面的参数信息(可能也包含HASH值)

let url = 'http://www.javascriptpekkk.cn/course/1935/task/114966/show?a=b&c=d#test'
  	function queryURLParams() {
		let obj = {};
		this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] =                      $2);            
		this.replace(/#([^?=&#]+)/g, (...[, $1]) => obj['HASH'] = $1);
		return obj;
	}

正向预查,实现大数字的千分符处理

		return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ',');
	}

split

let str = `content-encoding: gzip
 content-type: text/html; charset=utf-8
 date: Sat, 16 Jan 2021 09:17:16 GMT
 server: yunjiasu
 tracecode: 10366604600460907786011617
 vary: Accept-Encoding
 yjs-id: 9b71301e63182cf0-115`
 
 str = str.split(/(?:\n)/g);
 console.log(str)
 
 //['content-encoding: gzip', '    content-type: text/html; charset=utf-8', '    date: Sat, 16 Jan 2021 09:17:16 GMT', '    server: yunjiasu', '    tracecode: 10366604600460907786011617', '    vary: Accept-Encoding', '    yjs-id: 9b71301e63182cf0-115']