正则表达式

688 阅读9分钟

什么是正则表达式

  • 正则表达式(Regular Expression,常简写为regex、regexp或RE),又称正则表示式、正则表示法、规则表达式、常规表示法,是计算机科学的一个概念
  • 正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
  • 许多程序设计语言都支持利用正则表达式进行字符串操作。

简单概况:正则表达式是一种字符串匹配利器,可以帮助我们搜索、获取、替代字符串。

在JavaScript中,正则表达式使用RegExp类来创建,也有对应的字字面量的方式:

  • 正则表达式主要由两部分组成:模式(patterns)也可以叫规则和修饰符(flags)
       //创建正则
       //1> 匹配的规则pattern
       //2> 匹配的修饰符flags
       const re1 = new RegExp("abc","ig")
       const re2 = /abc/ig

使用字符串的方法,传入一个正则

1. match()

match() 方法用于查找字符串中与正则表达式匹配的所有部分,并返回一个数组。如果没有匹配的结果,则返回 null

用法:

  • 不带全局标志 gmatch 方法会返回第一个匹配结果,该结果是一个数组,数组的第一个元素是完整的匹配项,后续元素是捕获组的内容,同时还会包含 index(匹配项在原字符串中的起始位置)和 input(原始字符串)等属性。
  • 带有全局标志 gmatch 方法会返回一个包含所有匹配项的数组,不过这个数组里不会包含捕获组的信息。

关于捕获组后面会说明。

        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
        const nameRe = /《(.*?)》/g
        console.log(message.match(nameRe))

image.png

带有g之后:

image.png


2. replace()

replace() 方法用于替换字符串中与正则表达式匹配的部分(第一个参数也可以是个字符串,本文主要说明与正则相关的功能)。关于第二个参数,你可以传入一个新的字符串或者一个函数来进行替换。

用法:

        const mes = 'hello world world'
        let reg = /world/
        console.log(mes.replace(reg, 'JS')) //hello JS world
  • g 标志:如果添加了 g 标志,将替换所有匹配项,而不仅仅是第一个。

3. search()

search() 方法用于查找字符串中首次匹配正则表达式的位置。如果找到匹配项,返回匹配项的起始位置索引,否则返回 -1

用法:

const str = "The quick brown fox";
const regex = /quick/;
const result = str.search(regex);
console.log(result);  // 4 (匹配位置是索引 4)
  • 返回值:如果找到了匹配项,则返回匹配的起始位置;如果没有匹配项,则返回 -1

4. split()

split() 方法用于将字符串根据匹配正则表达式的内容进行分割,返回一个由分割后的子字符串组成的数组。

用法:


const str = "apple,banana,orange";
const regex = /,/;
const result = str.split(regex);
console.log(result);  // ["apple", "banana", "orange"]
  • 返回值:一个数组,包含字符串根据正则分割的部分。


6. matchAll()

matchAll() 方法返回一个迭代器,它提供字符串中所有与正则表达式匹配的部分,返回的是一个包含所有匹配信息的迭代器。

用法:


const str = "a1b2c3d4";
const regex = /\d/g;  // 匹配所有数字
const result = str.matchAll(regex);
for (const match of result) {
    console.log(match[0]);
}
// 输出: 1,2,3,4
  • 返回值:返回一个迭代器,可以用来遍历所有匹配项。

使用正则对象上的实例方法

1. test()

  • 功能:检测字符串是否符合正则表达式的规则。
  • 返回值:布尔值(true 或 false)。
  1. exec()
  • 功能:在字符串中执行搜索,返回第一个匹配的结果。

  • 返回值

    • 如果找到匹配项,返回一个数组(包含匹配的子串及捕获组信息)。
    • 如果没有找到匹配项,返回 null

若正则表达式带有全局标志 gexec 方法会在每次调用时从上一次匹配结束的位置继续查找下一个匹配结果。通过多次调用 exec,可以遍历字符串中所有的匹配项。

        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》";
        const nameRe = /《(.*?)》/g

        let result;
        while ((result = nameRe.exec(message)) !== null) {
            console.log(result[0]);
        }
        //《黄金时代》
        //《沉默的大多数》

当正则表达式没有全局标志 g 时,它只会返回第一个匹配结果。

        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》";
        const nameRe = /《(.*?)》/ // 无全局标志 g

        let result = nameRe.exec(message)
        console.log(result[0]) //只会匹配第一个  《黄金时代》

注意: 若正则没加g,不要再while中使用,会无限循环。

正则表达式规则

常见的修饰符

修饰符作用
g全局匹配,匹配所有符合的内容
i忽略大小写匹配
m多行匹配,^$ 作用于每一行

规则-字符类

字符类是一个特殊的符号,匹配特定集中的任何符号。

字符类作用记忆规则
\d匹配数字(0-9d 来自 digit(数字)。
\D匹配非数字([^0-9]D 是 \d 的反义,表示 not digit
\w匹配单词字符(字母、数字、下划线 [a-zA-Z0-9_]w 来自 word(单词)。
\W匹配非单词字符([^a-zA-Z0-9_]W 是 \w 的反义,表示 not word
\s匹配空白字符(空格、制表符 \t、换行符 \n 等)s 来自 space(空白)。
\S匹配非空白字符S 是 \s 的反义,表示 not space
.匹配任意字符(除换行符,若加 s 修饰符可匹配换行符). 是通配符,代表“any character”。

规则-锚点和词边界

锚点用于匹配字符串中的特定位置,而不是字符本身。

符号作用
^匹配 字符串或行的开头
$匹配 字符串或行的结尾

词边界用于匹配单词的开头或结尾,而非单词字符(如空格、标点符号)标识单词的边界。

符号作用
\b匹配单词边界(单词的开头或结尾)
\B匹配非单词边界

规则-转义字符

如果要把特殊字符作为常规字符来使用,需要对其进行转义。只需要在它前面加个反斜杠。

常见的需要转义的字符: [] \ ^ $ ? * + ( )等。

规则-集合和范围

1. 集合(Character Sets)

集合通过方括号 [] 来定义,表示匹配方括号内的任意一个字符。集合是“或”的关系,意味着它会匹配其中的任何一个字符。

常见集合规则

  • [abc]:匹配字符 abc 中的任意一个字符。

  • [aeiou]:匹配小写字母中的任意一个元音字符。

  • [^abc]:匹配除 abc 以外的任何字符(即否定集合)。

2. 范围(Ranges)

范围用于定义字符的连续范围,通常与集合一起使用来表示匹配某个范围内的字符。范围内的字符按字母或数字的顺序排列。

常见范围规则

  • [a-z]:匹配任意小写字母。
  • [A-Z]:匹配任意大写字母。
  • [0-9]:匹配任意数字,和\d相同。
  • [a-zA-Z0-9]:匹配任意字母(大小写均可)或数字,和\w相同。

规则-量词

量词 用于指定前面的 字符或表达式 出现的次数,可以控制匹配的 最小最大 次数。

常见的量词

量词含义
*匹配 0 次或多次(等价于 {0,}
+匹配 1 次或多次(等价于 {1,}
?匹配 0 次或 1 次(等价于 {0,1},表示“可选”)
{n}匹配恰好 n 次
{n,}匹配至少 n 次
{n,m}匹配 n 到 m 次(包括 n 和 m)

规则-贪婪和惰性模式

在 JavaScript 的正则表达式中,默认情况下 量词(*+{n,m} 等)贪婪模式(Greedy) ,即尽可能多地匹配字符。
如果我们想让匹配尽可能少,可以使用 惰性模式(Lazy) ,即在量词后面加上 ?,使匹配变得最小化。


1. 贪婪模式(Greedy)

默认情况下,正则表达式会尽可能多地匹配字符,直到整个模式不匹配为止。

        const message="我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
        const nameRe=/《.*》/ig
        console.log(message.match(nameRe))
        //['《黄金时代》和《沉默的大多数》']

🔹 .* 贪婪匹配,会尽可能匹配多的字符,从 第一个 最后一个


2. 惰性模式(Lazy)

如果在量词后面加上 ?,匹配就会变成惰性模式,即尽可能少地匹配字符

        const message="我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
        const nameRe=/《.*?》/ig
        console.log(message.match(nameRe))
        //['《黄金时代》', '《沉默的大多数》']

🔹 .*? 惰性匹配,会匹配尽可能少的字符,从 第一个 最早出现的

规则-捕获器和或操作

1.捕获组

  • 模式的一部分可以用括号括起来(...),这称为"捕获组"
  • 这主要有两个作用:
    • 它允许将匹配的一部分作为结果数组中的单独项。
    • 它将括号视为一个整体。

第一个作用:

match 方法在正则表达式没有全局标志 g 时,会返回一个包含完整匹配项和捕获组的数组;当正则表达式带有全局标志 g 时,只会返回所有匹配项,会忽略捕获组。

        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
        const nameRe = /(《)(.*?)(》)/
        console.log(message.match(nameRe))

image.png


        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
       const nameRe = /(《)(.*?)(》)/g
       console.log(message.match(nameRe))

image.png

所以一般配合  matchAll 使用

matchAll 方法会返回一个迭代器,迭代器中的每个元素都是一个包含完整匹配项和捕获组的数组。正则表达式必须有全局标志 g,否则会报错。

        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
        const nameRe = /(《)(.*?)(》)/g
        const result = message.matchAll(nameRe)
        // console.log(result)
        for(let item of result){
            console.log(item) 
        }

image.png

第二个作用:

        const info = "dfabcabcfabcdfdabcabcabcljcbacballnbanba;jk;j"
        //连续的abc至少出现两次
        const abcReg = /(abc){2,}/g
        console.log(info.match(abcReg))

image.png

  • 命名组:
    • 用数字记录组很困难。
    • 对于更复杂的模式,计算括号很不方便。我们有一个更好的选择:给活号起个名字。
    • 这是通过在开始括号之后立即放置?<name>来完成的。
        const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》"
        const nameRe = /(?<start>《)(?<name>.*?)(?<end>》)/
        console.log(message.match(nameRe))

image.png

2.或操作

  • |表示“或”操作,即匹配多个选项中的任意一个。
  • 通常和捕获组一起使用。
        const info = "dfabcabcfabcdfdabcabcabcljcbacballnbanba;jk;j"
        //连续的abc或者cba或者nba至少出现两次
        const abcReg = /(abc|cba|nba){2,}/g
        console.log(info.match(abcReg))

image.png

练习

1.写一个compile方法,完成模板的编译

        let template = '我是{{name}},年龄{{age}},性别{{sex}}'
        let person = {
            name: '张三',
            age: 25,
            sex: '男'
        }

方法1:

        function compile(template, data) {
            let reg = /\{\{(\w+)\}\}/
            while(reg.test(template)) {
                // let key = reg.exec(template)[1]
                let key = template.match(reg)[1]
                let value = data[key]
                template = template.replace(reg, value)
            }
            return template
        }

方法2:

        function compile(template, data) {
            let reg = /\{\{(\w+)\}\}/g 
            return template.replace(reg,(match,p1)=>{
                console.log(match,p1)
                //{{name}} name
                //{{age}} age
                //{{sex}} sex
                return p1 in data? data[p1] : ''
            })
        }

方法3:

        function compile(template, data) {
            const reg = /\{\{(\w+)\}\}/;
            if (reg.test(template)) {
                const key = reg.exec(template)[1]; 
                template = template.replace(reg, key in data ? data[key] : "")
                return compile(template, data)
            } else {
                return template; // 退出条件
            }
        }

image.png