Javascript中的正则表达式详细解读

前言

本片文章是笔者个人对正则一些浅显理解,有什么错误之处,请各位留言区说明

什么是正则表达式

正则表达式就是可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕捉到(exec/match…)

正则表达式的两种创建方式

字面量创建方式

  • 两个斜杠之间包起来的,都是用来描述规则的元字符
let reg = /\d+/;  //=>\d代表0-9之间的数字

基于构造函数模式创建

  • 有两个参数:1.元字符字符串;2.修饰符字符串(什么是元字符和修饰符后面会解释)
let reg1 = new RegExp('\\d+'); //=>/\d+/;
let reg2 = new RegExp('\\d+','g') //=>/\d+/g
  • 有个小知识点需要注意下:“\”在字符串中也有特殊含义,表示转义字符

正则表达式的组成

由元字符和修饰符组成

常用元字符

  1. 量词元字符
元字符含义
*零到多次
+一到多次
零次或者一次
{n}出现n次
{n,}出现n到多次
{n,m}出现n到m次
  1. 特殊元字符
元字符含义
\转移字符
.除\n(换行符)以外的任意字符
^以哪一个元字符作为开始
$以哪一个元字符作为结束
\n换行符
\d0~9之间的数字
\D非0~9之间的数字
\w数字、字母、下划线中的任意一个字符
\s一个空白字符(包含空格、制表符、换页符等)
\t一个制表符(一个tab键:四个空格)
\b匹配一个单词的边界
x|yx或者y中的一个字符
[^xy]除x/y以外的任意字符
[xyz]x或者y或者z中的一个字符
[a-z]指定a-z这个范围中的任意字符
[^a-z]非a-z中的任意字符
()正则中的分组符号
(?: )只匹配不捕获
(?!)正向否定预查
(?=)正向肯定预查
  1. 普通元字符
元字符含义
/asd/代表本身含义,匹配的就是字符串’asd’

常用修饰符

修饰符含义
i(ignoreCase)忽略单词大小写匹配
m(multiline)忽略换行符匹配即进行多行匹配
g(global)全局匹配

常用元字符详解

  1. ^ $
let reg = /^\d/;
console.log(reg.test("zhufeng")); // => false
console.log(reg.test("2020zhufeng")); // => true
console.log(reg.test("zhufeng2020")); // => false
let reg2 = /\d$/;
console.log(reg2.test("zhufeng")); // => false
console.log(reg2.test("2020zhufeng")); // => false
console.log(reg2.test("zhufeng2020")); // => true

// => ^/$两个都不加:字符串中包含符合规则的内容即可
let reg1 = /\d+/;
reg1.test("sssss2ssss"); // => true
// => ^/$都不加:字符串只能是和规则一致的内容
let reg2 = /^\d+$/;
reg2.test("202020"); // => true
reg2.test("2020ssss20"); // => false

// => 举个例子:验证手机号码(11位,第一个数字是1即可)
let reg3 = /^1\d{10}$/;
  1. \转义字符
// => .不是小数点,是除\n以外的任意字符
let reg = /^2.3$/;
console.log(reg.test("2.3")); // => true
console.log(reg.test("2@3")); // => true
console.log(reg.test("23")); // => false
// => 基于转移字符,让其只能代表小数点
let reg = /^2\.3$/;
console.log(reg.test("2.3")); // => true
console.log(reg.test("2@3")); // => false
console.log(reg.test("23")); // => false

// \在字符串中也有特殊含义
let str = "d";
let reg = /^\d$/;
console.log(rg.test(str)); // => false
reg = /^\\d$/;
reg.test(str); // => false
reg.test("\\d"); // => true
  1. x|y
let reg = /^18|29$/;
console.log(reg.test("18")); // => true
console.log(reg.test("29")); // => true
console.log(reg.test("129")); // => true
console.log(reg.test("1829")); // => true
console.log(reg.test("189")); // => true
console.log(reg.test("182")); // => true
// ---直接x|y会存在很乱的优先级问题,一般我们写的时候都伴随着小括号进行分组,因为小括号改变处理的优先级 => 小括号:分组
let reg = /^18|29$/;
console.log(reg.test("18")); // => true
console.log(reg.test("29")); // => true
console.log(reg.test("129")); // => false
console.log(reg.test("1829")); // => false
console.log(reg.test("189")); // => false
console.log(reg.test("182")); // => false
  1. []
// []:中括号中出现的字符大部分都代表本身的含义
let reg = /^[@+]+$/; // @或者+出现一到多次
reg.test("@@"); // => true
reg.test("@+"); // => true
// []中只有\d代表本身含义
let reg = /^[\d]$/;
reg.test("d"); // => false
reg.test("\\"); // => false
reg.test("9"); // => true
// []不存在多位数
let reg = /^[18]$/; // 1或者8
reg.test("1"); // => true
reg.test("8"); // => true
reg.test("18"); // => false

reg = /^[10-29]$/; // 1或者0-2或者9
reg.test("1"); // => true
reg.test("9"); // => true
reg.test("0"); // => true
reg.test("2"); // => true
reg.test("10"); // => false
  1. 正负向预查
let reg = /zhufeng/;
reg.test('zhufengsss')  //=>true
// 正向预查
let reg1 = /zhufeng(?=peixun)/ // zhufeng后面必须跟培训
reg1.test('zhufengsss')  //=>false
reg1.test('zhufengpeixun')  //=>true
reg1.test('zhufengpeixunssss') //=>true
reg1.test('zhufengssspeixunssss')  //=>false
// 负向预查
let reg = /zhufeng(?!peixun)/; //zhufeng后面必须不跟peixun
console.log(reg.test('zhufengpeixun')) // false
console.log(reg.test('zhufengpei')) // true

()小分组的作用

  1. 提升优先级
  2. 分组捕获
  3. 分组引用
// => 分组引用
let str = "book";
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; // => 分组引用就是通过“\数字”让其代表和对应分组出现一摸一样的内容
console.log(reg.test("look")); // true
console.log(reg.test("deep")); // true
console.log(reg.test("book")); // true
console.log(reg.test("sonn")); // false
// => 分组捕获
// => 先总后分 先左后右 先外后里
let str = 'zhu2019zhu2020zhu2021',
    reg = /\d{1,3}?/;
console.log(reg.exec(str));
reg = /(([a-z])+)((\d)+)/g;
// 这种括号套括号的情况,最小的分组永远捕获到的是符合条件的最后一项,也可以理解为把量词放在小括号外的,小分组捕获到的永远是整个分组的最后一项
console.log(reg.exec(str)); //=>["zhu2019", "zhu", "u", "2019", "9", index: 0, input: "zhu2019zhu2020zhu2021", groups: undefined]

?五大作用

  1. ?左边是非量词元字符:本身代表量词元字符。出现零到一次
  2. 问好左边是量词元字符:取消捕获时候的贪婪性
  3. (?: ) 只匹配不捕获
  4. (?=) 正向预查
  5. (?!) 负向预查

正则的捕获

  • 实现正则捕获的方法

    1. 正则 RegExp.prototype 上的方法
      • exec

        基于 exec 实现正则的捕获
        捕获到的结果是 null 或者一个数组
        第一项:本次捕获到的内容
        其余项:对应小分组本次单独捕获的内容
        index:当前捕获内容在字符串中的起始索引
        input:原始字符串

      • test

        RegExp.$1~RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组的信息
        一般不怎么用

    2. 字符串 String.prototype 上支持正则表达式处理的方法
      • replace

        @params:第一个参数正则对象,第二个参数普通字符串或者回调函数,如果是回调函数,把每次捕获到的内容作为实参传递给回调函数,把回调函数执行的返回值作为替换正则匹配的内容;
        @return:被替换后的字符串。

      • match

        @params:正则对象(如果参数是非正则表达式对象,会默认 new RegExp(obj))
        @return:数组(如果参数为空返回[’’];)
        @如果传的正则表达式是全局捕获,则返回大正则每次匹配的内容,不会返回小分组匹配的内容
        @如果不是全局捕获,返回第一次与大正则匹配的内容及其分组匹配的内容

      • split

        @params:正则对象
        @return:基于正则捕获到的所有字符分割后的数组

      • search

        @params:正则对象,如果传入的是一个非正则表达式对象 obj,则会隐式的基于 new RegExp(obj)进行转换
        @return:第一次匹配成功对应的索引,否则返回-1;

    // 基于exec进行捕获
    let reg = /\d+/;
    let str = 'asc1021dad201';
    console.log(reg.lastIndex); //=>0
    console.log(reg.exec(str)) //=>["1021", index: 3, input: "asc1021dad201", groups: undefined]
    console.log(reg.lastIndex); //=>0 第一次捕获后lastIndex的值没有改变所以,所以下一次捕获依然是从字符串最开始进行捕获
    // 懒惰性的原因:默认情况下lastIndex的值不会进行修改,所以每一次都是从最开始进行捕获,任何捕获方法都是进行
    // 解决方案:加全局捕获g
    console.log(reg.exec(str)) //=>["1021", index: 3, input: "asc1021dad201", groups: undefined]
    //====================================
    // 基于test进行捕获
    let str = "{0}{1}{2}let reg = /\{(\d+)\}/g;
    console.log(reg.test(str))  //=>true
    console.log(RegExp.$1); //=>'0'
    
    console.log(reg.test(str))  //=>true
    console.log(RegExp.$1); //=>'1'
    
    console.log(reg.test(str))  //=>true
    console.log(RegExp.$1); //=>'2'
    
    console.log(reg.test(str))  //=>false
    console.log(RegExp.$1); //=>'2' 存储的是上一次捕获的结果
    //=>RegExp.$1~RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组的信息
    //==============================
    // 基于match进行捕获
    let str = 'asc1021dad201';
    str.match(/\d+/);  //=>["1021", index: 3, input: "asc1021dad201", groups: undefined]
    let str = 'asc1021dad201';
    str.match(/\d+/g); //=>["1021", "201"]
    // match多次匹配的情况下,match只能把大正则匹配的内容捕获到,小分组匹配的信息无法获取
    let str = 'abc123adc456';
    str.match(/[a-z](\d+)/g);  //=>["c123", "c456"]
    //============================================
    // 基于replace捕获:即把捕获到内容进行替换,第二个参数可以传递回调函函数,可以把正则表达式每次捕获到的内容传递给回调函数,进行相应的处理。然后把回调函数每次执行的返回值拿来替换捕获到的内容。
    let time = '123-456-789';
    time = time.replace(/-/g,'');
    console.log(time)  //=> '123456789'
    //=================================
    // 基于split进行捕获:即可以选定多个字符进行替换
    let str = 'dadw#frr%dd#frgg%3223s';
    let ary = str.split(/[#%]/g);
    console.log(ary); //=>["dadw", "frr", "dd", "frgg", "3223s"];
    //==================================
    // 基于search捕获
    let str = "dadA1452";
    console.log(str.search(/[^a-zA-Z]/g));  //=>4
    

正则捕获的懒惰性

每执行一次 exec 只能捕获到一个符合正则规则的,但是默认情况下执行多少次,获取的结果永远都是第一个匹配到的,其余的捕获不到,即正则捕获的懒惰性:默认只捕获一个

  • 懒惰性的原因:默认情况下 lastIndex 的值不会被修改,每一次都是从字符串开始位置找,所以找到的永远是第一个符合规则的(任何捕获方法都是这样)
  • 解决正则的懒惰性用全局匹配 g

正则捕获的贪婪性

默认情况下正则捕获的时候,是按照当前正则所匹配的最长结果来获取的

  • 解决贪婪性:把后面的量词元字符去掉或者在量词元字符后面加?;
let str = 'sss2010ddd1855';
let reg = /\d+/;
console.log(reg.exec(str)); //=>["2010", index: 3, input: "sss2010ddd1855", groups: undefined]  2010中2,20,201都符合/\d+/这个条件但是却匹配到了2010,这就是正则匹配的贪婪性
// 解决方案一
reg = /\d/
console.log(reg.exec(str)); //=>["2", index: 3, input: "sss2010ddd1855", groups: undefined];
// 解决方法二
reg = /\d+?/g
conosle.log(reg.exec(str)); //=>["2", index: 3, input: "sss2010ddd1855", groups: undefined]

正则表达式中的分组捕获

// => 身份证号码
let str = "620421196608034816";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec(str)); // => ["620421199410102456", "620421", "1994", "10", "10", "5", index: 0, input: "620421199410102456", groups: undefined]
console.log(str.match(reg)); // => ["620421199410102456", "620421", "1994", "10", "10", "5", index: 0, input: "620421199410102456", groups: undefined]
// =>第一项:大正则匹配结果
// =>其余项:每一个小分组单独匹配捕获的结果
// =>如果设置了分组(改变优先级),但是捕获的时候不需要单独捕获,可以基于?:来处理

正则表达式的一些小案例

以下是几个比较常见的案例,也是比较基础,各位看官要是有什么更好的方法,欢迎分享

验证身份证号码

/* 
 *身份证号码分析
 *  前六位:省市县,开头1-9
 * 	年:19xx ~- 20xx
 *  月:01-09 10-12
 *  日:01-09 10-29 29-31
 *  倒数第二位性别:奇数男,偶数女
 */
let str = '620421199408034816';
let reg = /^([1-9]\d{5})((19|20)\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{2}(\d)(?:\d|X)$/gi;
console.log(reg.exec(str)); //=>["620421199408034816", "620421", "1994", "19", "08", "03", "1", index: 0, input: "620421199408034816", groups: undefined]

验证密码

要求:必须包含大小写和数字

let str = 'dwqqcq24234ffwA#';
let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\w\W]{8,18}$/;
console.log(reg.test(str));  //=>true

千分符

let str= '1874546577897446';
let reg = /\d{1,3}(?=(\d{3})+$)/g
/*  
*为了方便分析我们匹配‘0123456789’
* \d贪婪匹配到012,进入正向肯定预查345、678,9结尾不符合结尾条件,匹配失败,回溯
* \d贪婪匹配到01,进入正向肯定预查234、567,89不符合结尾条件,匹配失败,回溯
* \d匹配0,进入正向肯定预查123,456,789,789符合结尾条件,匹配成功,lastIndex值修改进入下一轮
* ["0", "789", index: 0, input: "0123456789", groups: undefined]
* \d贪婪匹配123,进入正向肯定预查456,789,789符合结尾条件,匹配成功,lastIndex值修改进入下一轮
* ["123", "789", index: 0, input: "0123456789", groups: undefined]
* \d贪婪匹配456,进入正向肯定预查789,789符合结尾条件,匹配成功,lastIndex值修改,进入下一轮
* ["456", "789", index: 0, input: "0123456789", groups: undefined]
* \d匹配789,进入正向肯定预查匹配到‘’,失败,
* null;
* 所以返回0、123、456
*/
str = str.replace(reg,$1=>$1+',');
console.log(str); //=>1,874,546,577,897,446

url参数处理

let url = 'http://baidu.com?aa=222&dd=www&333=sss&_=der#box';
let queryURLParams = function queryURLParams(url) {
    let obj= {};
    url.replace(/([^#&=?]+)=([^#&=?]+)/g,(...[_,$1,$2])=>obj[$1]=$2);
    url.replace(/#([^#&=?]+)/g,(...[_,$1])=>obj.HASH=$1);
    return obj;
}
console.log(queryURLParams(url)); //=>{333: "sss", aa: "222", dd: "www", _: "der", HASH: "box"}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值