让你彻底领悟正则表达式

什么是正则表达式

正则是一个用来处理字符串的规则

  1. 正则只能用来处理字符串
  2. 处理一些包含两个方面:
    ① 验证当前字符串是否符合某个规则 正则匹配
    ② 把一个字符串中符合规则的字符获取到 正则捕获

###正则表达式的创建

let reg1 = /^\d+$/g; //=>字面量创建
let reg2 = new RegExp("^\\d+$","g"); //=>构造函数方式

###元字符

正则两个斜杠之间包起来的都是“元字符” ,斜杠后面出现的都是“修饰符”

运算符 描述
\ 转移符(在一个普通字符转义为特殊的字符,例如:\d,把有特殊含义转换为普通意思,例如:\. 此处的点就不是任意字符,而是一个小数点)
^ 以什么开头
$ 以什么结尾
. 除了 \n 以外的任意字符
\n 匹配一个换行符
( ) 正则分组,把一个大正分为几个小正则
( ?: ) 正则分组,只匹配不捕获
( ?= ) 正向预查
( ?! ) 负向预查
x | y x或者y中的其中一个
[xyz] x或者y或者z中的其中一个
[^xyz] 除了这三个以外的任意一个字符
[a-z] a-z之间的任意一个字符
[^a-z] 除了a-z以外的任意字符
\d 0-9之间的一个数字
\D 除了0-9之间的任意字符
\s 匹配一个空白字符、空格、制表符、换页符…
\S 匹配任何非空白字符
\b 匹配一个边界符,'ni'(z左边和i右边是边界)'ni-hao'(z左边i右边,h左边o右边是边界)
\B 匹配非边界符
\w 匹配 数字、字母、下划线 中的任意一个 等价于 [0-9a-zA-Z]

###普通元字符

只要在正则中出现的元字符(在基于字面量方式创建),除了特殊和有量词意义的以外,其余的都是普通元字符

###量词

运算符 描述
* 出现零到多次
出现零到一次
+ 出现一到多次
{ n } 出现n次
{ n, } 出现n到多次
{ n, m } 出现n到m次

###修饰符

运算符 描述
i 忽略大小写
m 表示多行模式
g 匹配全局变量

###正则表达式中的几个规则:

####中括号的一些细节

  1. 中括号出现的元字符一般都是代表本身的含义
  2. 中括号中出现的两位数,不是两位数,而是两个数字中的任意一个

代码实例

let reg = /^.$/ //=>一个正则设置了^和$,那么代表的含义其实就是只有xxx
console.log(reg.test('n')); //true
console.log(reg.test('1')); //true

let reg = /^[.]$/ //=>中括号里面的元字符代表本身
console.log(reg.test('n')); //false
console.log(reg.test('1')); //false
console.log(reg.test('.')); //true

let reg = /^[18]$/ //=>中括号里面的两位数代表两个数
console.log(reg.test('18')); //false
console.log(reg.test('1'));  //true
console.log(reg.test('8'));  //true

let reg = /^[12-65]$/ // 这个正则的意思1或者2-6或者5
console.log(reg.test('13'));  //false 不是12~65
console.log(reg.test('6'));   //true

//年龄18-65岁之间的数
18~19 1[89]
20-59 [2-5]\d
60-65 6[0-5]
let reg = /^((1[8-9])|([2-5]\d)|(6[0-5]))$/;

//匹配[object aaa]
let reg = /^\[object .+\]$/;

####小括号分组作用

  1. 改变默认优先级
  2. 分组捕获
  3. 分组引用

第一、改变默认优先级

let reg = /^18|19$/
console.log(reg.test('18'));  //true
console.log(reg.test('19'));  //true
console.log(reg.test('1819'));//true
console.log(reg.test('189')); //true
console.log(reg.test('181')); //true
console.log(reg.test('819')); //true
console.log(reg.test('119')); //true

let reg = /^(18|19)$/
console.log(reg.test('18'));  //true
console.log(reg.test('19'));  //true
console.log(reg.test('1819'));//false
console.log(reg.test('189')); //false
console.log(reg.test('181')); //false
console.log(reg.test('819')); //false
console.log(reg.test('119')); //false

第二、分组引用

let reg = /^([a-z])([a-z])\2\1$/; //=>正则中出现的\1代表和第一分组出现一模一样的内容...
console.log(reg.test('oppo'));  //true
console.log(reg.test('poop'));  //true

第三、分组捕获

// 编写一个正则匹配身份证号码
let reg = /^\d{17}(\d|X)$/; //简单:只匹配知否符合格式,不能提取身份证中的一些信息 
let reg = /^\(d{6})(d{4})(d{2})(d{2})(d{2})(\d)(?:\d|X)$/
console.log(reg.exec('130987200198983789'));  //实现正则捕获

####问号在正则中的作用

  1. 量词元字符:出现零次或者一次 /-?/- 出现一次或者不出现
  2. 取消贪婪性:/\d+?/ 捕获时候只捕获最短匹配的内容
  3. 只匹配不捕获:?:
  4. 正向预查:?=
  5. 负向预查:?!

###正则捕获

把一个字符串中和正则匹配的部分捕获到

####正则方法

exec(正则捕获)

  1. 实现的是正则捕获,获取的结果是一个数组,如果不匹配获取的结果是 null
  2. 如果想获取大正则中部分信息,我们可以把这部分使用小括号包起来,形成一个分组,这样在捕获的时候,不仅可以把大正则匹配内容捕获到,而且每一个小分组中的内容也捕获到了(分组捕获)。
  3. 有时写小分组不是为了捕获信息,只是为了改变优先级或者进行分组引用,此时可以在分组的前面加上 ?: ,代表只匹配,不捕获。

test(字符串验证):验证字符串是否符合某个规格的正则

基于exec 可以实现正则的捕获

  • 如果当前正则和字符串不匹配,捕获的结果是 null
    • 如果匹配,捕获的结果是一个数组
      0:大正则捕获的内容
      index:正则捕获的起始索引
      input:原始操作的字符串
    • 执行一次exec只能捕获到第一个和正则匹配的内容(正则的懒惰性)

代码实例

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

基于test匹配机制

基于test 匹配的时候,如果设置了g test 匹配也相当于捕获,修改了 lastIndex

代码实例

let str = "nihao2018nihao2019";
let reg = /\d+/g; 
console.log(reg.test(str)); //=>true
console.log(reg.lastIndex); //=>6
console.log(reg.exec(str)); //=>2019

####正则的懒惰性和贪婪性
正则懒惰性

每一次捕获的时候,只会捕获符合正则的第一项,后面符合正则规则的也不会再继续捕获 。
解决正则捕获懒惰性,我们需要加全局修饰符 g

代码实例

let str = "nihao2018nihao2019";
let reg = /\d+/g;
console.log(reg.lastIndex) //0
console.log(reg.exec(str));//['2018']
console.log(reg.lastIndex) //9
console.log(reg.exec(str));['2019']
console.log(reg.lastIndex) //18
console.log(reg.exec(str));//null
console.log(reg.lastIndex) //0
console.log(reg.exec(str));//['2018']

正则贪婪性

每一次捕获的时候,总是捕获到和正则匹配中最长的内容,例如:2 符合 \d+ 2666 也符合 \d+ ,但捕获的是最长的内容 2666
解决方案:在量词元字符后面加 ?

代码实例

let str = "nihao2018nihao2019";
let reg = /\d+?/g; 
console.log(reg.exec(str)); //=>['2']

####字符串方法

replace(字符串替换)
字符串replace实现正则捕获的方法(本身是字符串替换)

split(字符串分割)
把符合正则规则的内容分割(去除)成数组

match(字符串捕获)
字符串 match 方法,捕获符合正则规则的内容

正则捕获局限性: 在正则捕获的时候存在分组,捕获的时候不仅仅捕获把大正则匹配到的字符捕获到(数组第一项),而且把小分组匹配的内容也单独抽取出来(数组中的第二项开始就是小分组捕获的内容——分组捕获),而 ?: 是用来阻止分组捕获内容的,“只匹配不捕获”。

第一、match捕获

代码实例

let str = "nihao2018nihao2019";
let reg = /\d+/g;
str.match(reg); //=>["2018", "2019"]

**match方法局限性:**match捕获的内容只有大正则捕获的,小分组内容没有单独抽取出来。

let str = "nihao{2018}nihao{2019}";
let reg = /\{(\d+)\}/g; 
console.log(reg.exec(str));  //=>["{2018}", "2018", index: 5, input: "nihao{2018}nihao{2019}", groups: undefined]

console.log(str.match(reg)); //=>["{2018}", "{2019}"] match方法也有自己的局限性,在正则设置了g的情况下,基于match捕获的内容只有大正则捕获的,小分组内容没有单独抽取出来。

第二、split捕获

把匹配符合正则规则的字符串分割成数组

代码示例

let str = '2019/5/20 18:30:00',
    ary = str.split(/(?:\/| |:)/g); //=> 匹配 "/"、" "、":" 作为分割
console.log(ary); //=>["2019", "5", "20", "18", "30", "00"]

第三、replace捕获

replace实现正则捕获的方法(本身是字符串替换)

代码实例

//将nihao替换成你好
let str = "nihao2018nihao2019";
str.replace(/nihao/g,'你好');

replace 原理

let str = "nihao{val:2018}nihao{val:2019}",
	reg = /\{val:(\d+)\}/g
str.replace(reg,"@"); //=>每一次把当前“大正则”匹配的结果用第二个传递的字符串替换掉
console.log(str); //=>'nihao@nihao@'

str.replace(reg,"$1"); //=>$1不是拿这个字符串替换掉大正则匹配的内容,此处的$1代表第一个分组匹配的内容,等价于:RegExp.$1
console.log(str); //=>'nihao2018nihao2019'

/*
 1. reg 和 str 匹配多少次,函数就触发了多少次,而且传递了一些参数信息值
 2. 每一次arg中存储的信息,和执行exec捕获的信息相似(内置原理:每一次正则匹配到结果,都把函数执行,然后基于exec把本次匹配的信息捕获到,然后把捕获的信息传递个这个函数)
 3. 每一次函数中返回的是什么,就把大正则匹配的内容替换成什么
*/
str.replace(reg,(...rag)=>{
	console.log(arg);
	return 'AA';
});
console.log(str); //=> 'nihaoAAnihaoAA'

综合练习

//事件字符串格式化 "2019/4/30 18:30:23" => "04-30 17:50"
//方法一:获取时间字符串中所有数字(split)
let str = '2019/4/30 18:30:23';
ary = str.split(/(?:\/| |:)/g);
//function fn (time) {return time<10?'0'+time:null;}
let [,month,day,hours,minutes] = ary,
	//result = `fn(${month})-fn(${day}) fn(${hours}):fn(${minutes})`;
	result = `${month}-${day} ${hours}:${minutes}`;
	console.log(result)

//方法二:
/*
  1. 利用match方法获取到时间数组;
  2. 指定最后想要的时间格式,我们基于这个数组中的内容拼接好即可
*/

let str = '2019/4/30 18:30:23';
//map相对于forEach来讲多了返回值,函数中return是什么,就是当做数组迭代的这一项替换成什么。
let ary = str.match(/\d+/g).map(item=>{
	return item<10?'0'+item:item;
});
let timeFormat = '{0}年{1}月{2}日 {3}时{4}分{5}秒';

timeFormat.replace(/\{\d\}/g,(...arg)=>{
	let [,index] = arg;
	return ary[index];
});

//在正则原型上扩展一个方法,把符合正则规则的捕获到
RegExp.prototype.myExecAll = function(str) {
    let result = [],
        ExecAry = this.exec(str);
    //为了防止出现死循环:我们检测一下正则是否加g,没有加g只把第一次捕获的返回即可。
    if (!this.global) {
        return this.exec(str);
    }
    while (ExecAry) {
        result.push(ExecAry[0]);
        ExecAry = this.exce(str);
    }
    return result;
}
console.log(reg.myExecAll(str));

//在String原型上增加一个格式化时间格式方法
String.prototype.myTimeFormat = function myTimeFormat(template = '{0}年{1}月{2}日 {3}时{4}分{5}秒') {
    let ary = this.match(/\d+/g).map((item => item < 10 ? "0" + item : item));
    return template.replace(/\{(\d)}/g, (...[, index]) => ary[index] || '00'); // 如果没有则为00代替
}

let str = '2019/4/30 18:30:23';
console.log(str.myTimeFormat('{1}月{2}日')); //04月30日
console.log(str.myTimeFormat('{1}-{2} {3}:{4}')); //04-30 18:30

###常用的正则表达式

//匹配身份证号码
let reg = /^\(d{6})(d{4})(d{2})(d{2})(d{2})(\d)(?:\d|X)$/
console.log(reg.exec('130987200198983789'));  //实现正则捕获

//匹配有效数字正则表达式
/*分析规则
	1. 可以出现+/-号:可以没有,也可以有一个
	2. 整数一位或者多位,多位数不能以0开头
	3. 小数部分:可能有可能没有,有小数点后面至少要跟一位数字
*/
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;

//正则手机号码验证
/*分析规则:
	1. 11位数字组成
	2. 以1开头
*/
let reg = /^1\d{10}&/;

//中文汉字
/*分析规则:
	1. 中文汉字[\u4E00-\u9FA5]
*/
let reg = /^[\u4E00-\u9FA5]{2,}(·[\u4E00-\u9FA5]{2,})?&/;


//邮箱
/*分析规则:
	1. [email protected]
	2. 第一部分:数字、字母、下划线、"-" 、".",但是"-"和"."不能作为开头,不能连续出现 "-" 或者 "."
*/
let reg = /^\w+([-.]\w+)*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)&/;

//匹配前后有空格字母
let reg = /^ +| +$/g

你可能感兴趣的:(让你彻底领悟正则表达式)