正则表达式学习 - 捕获 - 分组捕获

1 实现正则捕获的方法

  • 正则RegExp.prototype方法
    • exec: 一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回 null)。
    • test: 一个在字符串中测试是否匹配的RegExp方法,它返回 true 或 false。
  • 字符串String.prototype方法
    • replace: 一个在字符串中执行查找匹配的String方法,并且使用替换字符串替换掉匹配到的子字符串。
    • match: 一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null。
    • matchAll: 一个在字符串中执行查找所有匹配的String方法,它返回一个迭代器(iterator)。
    • split: 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法。
    • search: 一个在字符串中测试匹配的String方法,它返回匹配到的位置索引,或者在失败时返回-1。

2. 方法解析

2.1 exec方法

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

// 例
let str = 'sdfsadf12354asdfas412456aasdf';
let reg = /\d+/; // 捕获的正则表达式需要能过验证通过捕获对象字符串的
console.log(reg.exec(str)); // => ["12354", index: 7, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]

/*
    * 结果: 
    * 1. 捕获到的结果是null(正则验证false) 或者一个数组
    *    第一项: 本次捕获到的内容
    *    其余项: 对应小分组本次单独捕获的内容
    *    index: 当前捕获内容在字符串中的起始索引
    *    input: 原始字符串
    * 2. 每执行一次exec, 只能捕获到一个符合正则规则的 => 称之为'正则捕获的懒惰性': 默认只捕获第一个
*/

**懒惰性捕获的原因: **

  • 懒惰性捕获的原因: 默认情况下lastIndex的值不会被修改, 每一次都是从字符串开始位置查找, 所以找到的元素永远只是第一个

解决方法:

  • 设置全局匹配修饰符g后, 会自动修正lastIndex的值(手动修改是无用的)
// 例
let str = 'sdfsadf12354asdfas412456aasdf';
let reg = /\d+/g; // 捕获的正则表达式需要能过验证通过捕获对象字符串的
/*
    * reg.lastIndex: 当前正则下一次匹配的起始索引位置
    *  懒惰性捕获的原因: 默认情况下lastIndex的值不会被修改, 每一次都是从字符串开始位置查找, 所以找到的元素永远只是第一个
    * 直接修改lastIndex的值是无效的
    * 通过设置全局匹配修饰符g, 会自动修正lastIndex的值
*/
console.log(reg.exec(str)); // => ["12354", index: 7, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
console.log(reg.exec(str));// => ["412456", index: 18, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
console.log(reg.exec(str));// => null 当全部捕获后, 再次捕获的结果是null, 但是lastIndex又回归了初始值0, 再次捕获时又会从头开始 

2.2 test方法

test方法也会捕获, 但是不会返回捕获结果

  • 若是设置了全局修复符g, 使用test方法时会将lastIndex调整为下一个捕获的lastIndex

    // 例
    let str = 'sdfsadf12354asdfas412456aasdf';
    let reg = /\d+/g; // 捕获的正则表达式需要能过验证通过捕获对象字符串的
    // 用同一个正则先去验证字符串的话, 这样捕获会出现问题, 此时可以在用同一个正则规则去验证, 不要使用同一个正则对象
    if (reg.test(str)) {
        console.log(reg.lastIndex); // => 12 
        console.log(reg.exec(str)); // => ["412456", index: 18, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
    }
    

2.3 String.prototype.match方法: 捕获所有匹配组

这是字符串原型上的方法, 捕获所有匹配项

// 没有加修饰符g的时候, 直接返回第一匹配项
let reg = /\d+/;
let str = 'asf123asd124adf154adf';
str.match(reg) // => ["123", index: 3, input: "asf123asd124adf154adf", groups: undefined]

// ======================
// 返回所有匹配项
let reg = /\d+/g;
let str = 'asf123asd124adf154adf';
str.match(reg) // =>  ["123", "124", "154"]

// =======================
// 没有匹配项时, 返回null
let reg = /\d+/g;
let str = 'dfasdfsd';
str.match(reg) // => null 

2.4 String.prototype.replace: 字符串替换方法

  • 一般伴随正则一起使用 -- 正则可以全局替换

    // 将suiyue替换成岁月
    let str = 'suiyue@2019|suiyue@2020';
    /*
      * 不使用正则 => str.replace('suiyue', '岁月') => 一次只能替换一个
      * 使用正则 => str.replace(/suiyue/g, '岁月')
    */
    
    // ======================
    // 例子: 处理时间字符串
    let time = '2019-08-13';
    // => 处理为2019年08月13日
    let reg = /^(\d{4})-(\d{1,2})-(\d${1,2})$/;
    
    // 实现方法一: $1为分组捕获的第一个, $2为分组捕获的第二个... 
    time.replace(reg, '$1年$2月$3日');
    
    // 实现方法二: 
    time.replace(reg, (big, ...arg) => {
        // arg为剩余参数, 值为: $1-$9
        // $1为分组捕获的第一个, $2为分组捕获的第二个...  
    })
    
    // ================
    // 例子: 单词首字母大写
    let str = 'good good study. day day asdf';
    let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
    // 函数被执行了六次(相当于执行一次捕获一个), 每一次都把正则匹配信息传递给函数
    // => 每一次参数: ['good', 'g'] ['good', 'g'] ['study', 's']...
    str = str.replace(reg, (...arg) => {
        let [content, $1] = arg;
        $1 = $1.toUpperCase();
        conetnt = content.substring(1);
        return $1 + content;
    })
    console.log(str); // => Ggood Ggood Sstudy. Dday Dday Aasdf
    

3. 正则的分组捕获

  • 一次匹配下, 分组捕获

let str = '360145199011220112';
/*
小括号作用;
1. 改变优先级
2. 分组捕获 => 会将捕获的内容进行分组捕获
*/
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|x|X)/; console.log(reg.exec(str)); // => ["360145199011220112", "360145", "1990", "11", "22", "1", "2", index: 0, input: "360145199011220112", groups: undefined] // => 第一项: 大正则匹配的结果 // => 其余项: 每一个小分组单独匹配捕获的结果 // => 如果设置了分组, 只是为了改变优先级, 不需要分组捕获, 可以设置?:元字符 // 例如: /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|x|X)/


* 多次匹配下, match只能把大正则匹配的内容获取到, 小分组(小括号分组)匹配的信息无法获取

```javascript
let str = '{0}年{1}月{2}日';
let reg = /\{(\d+)\}/g;
// 使用match分组捕获时, 多次匹配无法捕获组
console.log(str.match(reg)); // => ["{0}", "{1}", "{2}"]
  • 分组的另一个作用: '分组引用'

    let str = 'book'; // 假设需要保证中间两个字母相同
    let reg = /^[a-zA-Z([a-zA-Z])\1[a-zA-Z]]$/; // =>  分组引用就是通过'\数字'让其代表和对应分组出现一模一样的内容
    console.log(reg.test('book)); // => true
    console.log(reg.test('deep)); // => true
    console.log(reg.test('some)); // => false
    

4. 正则捕获的贪婪性

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

    let str = 'sdf230asdf123';
    let reg = /\d+/g;
    // 2 和 3 和 0 ..应该都符合正则reg的, 但是因为正则捕获的贪婪性, 会按照最长结果来匹配
    console.log(str.match(reg)); // => ['230', '123'];
    
    // ===================== 
    // 在 量词元字符 后面设置? : 取消捕获时候的贪婪性(按照正则匹配的最短结果来获取)
    reg = /\d+?/g;
    console.log(str.match(reg)) // =>  ["2", "3", "0", "1", "2", "3"]
    
  • 问号(?)在正则中的作用:

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

5. 参考资料

B站-Web前端快速入门正则表达式2019最新正则教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350