思维导图 JavaScript 正则表达式之括号的魅力

本文深入探讨JavaScript正则表达式中的括号运用,包括分组(捕获型、非捕获型、前瞻型)和分支结构。通过实例分析,展示了括号在数据提取、替换操作以及反向引用中的关键作用。同时,提供了多个实战案例,如模拟trim方法、单词首字母大写、驼峰化字符串等,帮助理解括号在正则表达式中的威力。
摘要由CSDN通过智能技术生成

正则表达式,本质上也是一门语言,而括号的存在使得正则表达式语言更为强大

对于括号 ( ) 的使用,其最原始的功能就是 分组分支结构;而后在此基础上可以实现如:分组引用反向引用

括号最原始的功能就是 分组 和 分支结构;强调的是括号内的表达式是一个整体,即提供子表达式 

分组 和 分支结构 

[1] 分组 

分组通常由以下几种类型:

    捕获型分组 (p)

    非捕获型分组 (?:p )

    正向前瞻型分组 (?=p)  ,表示当前位置后面要有什么

    反向前瞻型分组 (?!p) ,表示当前位置后面不能有什么

其中,正向前瞻型和反向前瞻型分组其实就是位置匹配,分别是正向先行断言和负向先行断言;

/// 分组 (捕获型分组)
regex = /(ab)+/g
string = 'ababa abbbb abababa'
console.log( string.match(regex) )
// => [ 'abab', 'ab', 'ababab' ]
string.match(regex)
console.log( RegExp.$1 )
// => ab

 

/// 非捕获型分组
///   如果只想要括号最原始的功能,但不会引用它
regex = /(?:ab)+/g
string = 'ababa abbbb abababa'
console.log( string.match(regex) )
// => [ 'abab', 'ab', 'ababab' ]
string.match(regex)
console.log( RegExp.$1 )
// => 

[2] 分支结构

在多选分支结构中,括号提供了分支表达式的所有可能性;(表达式1|表达式2|表达式3)

/// 分支结构 (p1|p2)
regex = /^I LOVE (JINGJING|LIJING) FOREVER$/
console.log( regex.test('I LOVE LIJING FOREVER') )
console.log( regex.test('I LOVE JINGJING FOREVER') )

分组引用

其最重要的作用就是 数据提取更强大的替换操作

/// 1. 提取数据
string = "2021-01-27"
// 不带分组
regex = /\d{4}-\d{2}-\d{2}/
console.log( string.match(regex) )
// => [ '2021-01-27', index: 0, input: '2021-01-27', groups: undefined ]
// 带分组
regex = /(\d{4})-(\d{2})-(\d{2})/
console.log( string.match(regex) )
// => [ '2021-01-27', '2021', '01', '27', index: 0, input: '2021-01-27', groups: undefined ]

/// 使用正则实例对象的 exec 方法
console.log( regex.exec(string) )
// => [ '2021-01-27', '2021', '01', '27', index: 0, input: '2021-01-27', groups: undefined ]

/// 使用 $1 - $9 提取对应的分组数据
// regex.test( string )
// regex.exec(string)
string.match(regex)
console.log( RegExp.$1 )    // => 2021
console.log( RegExp.$2 )    // => 01
console.log( RegExp.$3 )    // => 27

 

/// 2. 替换操作
///  板栗儿:yyyy-mm-dd => mm/dd/yyyy

regex = /(\d{4})-(\d{2})-(\d{2})/
string = "2021-01-28"
let result = string.replace(regex, "$2/$3/$1")
console.log( result )
// => 01/28/2021

/// 等价于
result = string.replace( regex, function () {
    return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1
})
console.log( result )
// => 01/28/2021

/// 也等价于
result = string.replace( regex, (match, year, month, day) => {
    console.log( match, year, month, day)
    return `${month}/${day}/${year}`
})
console.log( result )
// 2021-01-28 2021 01 28
// 01/28/2021

反向引用

在正则本身里面引用分组,且只能引用之前出现的分组,即为反向引用;使用 \1, \2, \3 ... 进行分组编号的匹配,从而实现对应分组的引用;如果引用的分组不存在,则会直接匹配字符本身

/// 反向引用
///    在正则本身里引用分组,但只能引用之前出现的分组,即反向引用

/// 静静仙女:龙哥哥,现在有个想法,想写一个正则支持匹配如下三种格式
///   2021-01-27   2021/01/27   2021.01.27 
/// 憨憨龙:这个简单,请看:
regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/

console.log( regex.test('2021-01-27') )     // => true
console.log( regex.test('2021/01/27') )     // => true
console.log( regex.test('2021.01.27') )     // => true
console.log( regex.test('2021-01/27') )     // => true

/// 静静仙女:但是你这个同时也匹配了 2021-01/27 ,我想要保证分隔符前后一致
/// 憨憨龙:Nice! 这个也简单,使用反向引用 "\1" ( 其实就是匹配分组编号 \Group#1 ) 就可以解决,请看:
regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/

console.log( regex.test('2021-01-27') )     // => true
console.log( regex.test('2021/01/27') )     // => true
console.log( regex.test('2021.01.27') )     // => true
console.log( regex.test('2021-01/27') )     // => false

思维导图

案例分析

案例一:模拟字符串的 trim() 方法

/// 模拟实现字符串 trim 方法
///    trim() 方法是去掉字符串开头和结尾的空白字符

/// 方法一,直接匹配开头或结尾的空白字符 (效率高)
const trimFirst = str => {
    return str.replace(/^\s+|\s+$/g, '')
}
console.log( trimFirst("   aijingjing   ") )
// => aijingjing


/// 方法二,匹配整个字符串,用引用来提取相应的数据
const trimSecond = str => {
    return str.replace(/^\s*(.*?)\s*$/g, '$1')
}
console.log( trimSecond("    aijingjing         ") )
// => aijingjing
/// *? 惰性匹配,避免匹配到最后一个空格之前所有的空格,使得效率变得更低

案例二:将每个单词首字母转为大写

/// 将每个单词的首字母变成大写

const titleize = str => {
    // return str.toLowerCase().replace(/(^|\s)\w/g, (char) => {
    //     return char.toUpperCase()
    // })
    return str.toLowerCase().replace(/(?:^|\s)\w/g, (char) => {
        return char.toUpperCase()
    })
}

console.log( titleize('i love jingjing') )
// => I Love Jingjing
/// 这里其实不需要使用非捕获匹配,只是为了演示非捕获括号的作用

案例三:字符串驼峰化

板栗儿:'-moz-transform   '  =>  'MozTransform'

/// 驼峰化
let camelize = str => {
    return str.replace(/[-_\s]+(.)?/g, (match, char) => {
        return char ? char.toUpperCase() : ''
    })
}

console.log( camelize('-moz-transform  ') )
// => MozTransform

/// 思考:如果我不想去掉 - 或者 _ 怎么办?
camelize = str => {
    // 实现代码 ...
}

console.log( camelize('-moz-transform') )
// => -Moz-Transform

 案例四:连接字符化或中划线化

/// 中划线化 或 连接字符化
const dasherize = str => {
    return str.replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase()
}

console.log( dasherize('MozTransform') )
// => -moz-transform

案例五:XML 标签成对匹配

/// XML 标签成对匹配
/// 要求匹配:
///    <title>To JingJing</title>
///    <p>I LOVE YOU FOREVER!!!</p>
/// 不能匹配类似不成对的标签
///    <title>HAHAHAHAHA</p>

/// 分析:
/// 1, 匹配一个开标签,使用正则  <[^>]+>
/// 2, 撇配一个闭合标签,使用正则  <\/[^>]+>
/// 3, 成对匹配,可以使用反向引用
let regex = /<([^>]+)>[\d\D]*<\/\1>/
let string1 = "<title>To JingJing</title>"
let string2 = "<p>I LOVE YOU FOREVER!!!</p>"
let string3 = "<title>HAHAHAHAHA</p>"

console.log( regex.test(string1) )  // => true
console.log( regex.test(string2) )  // => true
console.log( regex.test(string3) )  // => false
/// HTML 转义 和 反转义

/// 将 HTML 特殊字符转换成对应的字符实体
const escapeHTML = str => {
    let escapeChars = {
        '<': 'lt',
        '>': 'gt',
        '"': 'quot',
        '&': 'amp',
        '\'': '#39' 
    }

    // 使用正则构造函数生成正则,然后替换相应的数据
    return str.replace( new RegExp(`[${Object.keys(escapeChars).join('')}]`, 'g'), (match) => {
        return `&${escapeChars[match]};`
    })
}

console.log( escapeHTML('<div>I LOVE MY LITTLE FAIRY</div>') )
// => &lt;div&gt;I LOVE MY LITTLE FAIRY&lt;/div&gt;


/// 将字符实体转换为对应的 HTML 字符
const unescapeHTML = str => {
    let htmlEntities = {
        nbsp: ' ',
        lt: '<',
        gt: '>',
        quot: '"',
        amp: '&',
        apos: '\''
    }
    return str.replace(/\&([^;]+);/g, (match, key) => {
        console.log(match, key)
        if (key in htmlEntities) {
            return htmlEntities[key]
        }
        return match
    })
}

console.log( unescapeHTML('&lt;div&gt;I LOVE MY LITTLE FAIRY&lt;/div&gt;') )
// => <div>I LOVE MY LITTLE FAIRY</div>

 

源代码地址:regular-expression

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值