正则表达式

什么是正则表达式?

◼ 我们先来看一下维基百科对正则表达式的解释:
  正则表达式(英语:Regular Expression,常简写为regex、regexp或RE),又称正则表示式、正则表示法、规则表达式、常规表示法,是计算机科学的一个概念;
  正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
  许多程序设计语言都支持利用正则表达式进行字符串操作。
◼ 简单概况:正则表达式是一种字符串匹配利器,可以帮助我们搜索、获取、替代字符串;
◼ 在JavaScript中,正则表达式使用RegExp类来创建,也有对应的字面量的方式:
  正则表达式主要由两部分组成:模式(patterns)和修饰符(flags)
const res1 = new RegExp("hello","i")
const res2 = /hello/i

正则表达式的使用方法

◼有了正则表达式我们要如何使用它呢?
  JavaScript中的正则表达式被用于RegExp的exec 和test 方法;
  也包括String 的match、matchAll、replace、search 和split 方法;
方法          描述
实例方法:
exec          一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回null)。
test          一个在字符串中测试是否匹配的RegExp 方法,它返回true 或false。
字符串方法:
match         一个在字符串中执行查找匹配的String 方法,它返回一个数组,在未匹配到时会返回null。
matchAll      一个在字符串中执行查找所有匹配的String 方法,它返回一个迭代器(iterator)
search        一个在字符串中测试匹配的String 方法,它返回匹配到的位置索引,或者在失败时返回-1。
replace       一个在字符串中执行查找匹配的String 方法,并且使用替换字符串替换掉匹配到的子字符串。
split         一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的String方法。

正则表达式的演练

  // const re1 = new RegExp("abc","ig")
    const re2 = /abc/ig
    const message = "fdabc123 faBc31231 dfABC1312 AaaBc1231"
    //使用正则表达式
     //1.使用正则对象上的实例方法
     //2.传入一个正则表达式
    // 需求将所有的abc(忽略大小写)换成cba
    //普通方式
   const newmessage =  message.replace("abc","cba")
   console.log(newmessage) // fdcba123 faBc31231 dfABC1312 AaaBc1231
   //使用正则表达式
   const newmessage1 = message.replace(/abc/ig,"cba")
   console.log(newmessage1) //fdcba123 fcba31231 dfcba1312 Aacba1231
    // 需求:将字符串所有的数字删除
    const newmessage2 = message.replace(/[0-9]+/ig,"")
    console.log(newmessage2)//fdabc faBc dfABC AaaBc

正则表达式的方法演练

   <!-- // 案例:让用户输入的账号必须包含5个a -->
  输入账号:<input type="text" placeholder="请输入5个a">
  <p class="tip"></p>
    //1.使用正则表达式对象的实例方法
    //webpack ->loader ->test:匹配文件名
    // 1.1 test方法:检测字符串是否符合正则的规则->返回布尔类型
    // 规则
    const re1 = /^a{5}$/ig//^表示开头是a $表示结尾是c 中间必须是b
    const iptEl = document.querySelector("input")
    const tipEl = document.querySelector(".tip")
    iptEl.onblur =function(){
      const message = iptEl.value
      if(re1.test(message)){
        tipEl.textContent = "符合5个a规则"
        tipEl.style = `color:green`
      }
      else{
        tipEl.textContent = "不符合5个a规则"
        tipEl.style = `color:red`
      }
    }
    //1.2 exec方法:使用正则执行一个字符串-->第一个匹配的结果返回
    const message1 = "fdabc123 faBc31231 dfABC1312 AaaBc1231"
    const re2 = /abc/ig
    const res2 = re2.exec(message1)
    console.log(res2)//['abc', index: 2, input: 'fdabc123 faBc31231 dfABC1312 AaaBc1231', groups: undefined]
    //1.3 match方法:字符串方法:匹配到所有的结果
    const res3 = message1.match(re2)
    console.log(res3)//(4) ['abc', 'aBc', 'ABC', 'aBc']
    //1.4 matchAll 方法:返回一个迭代器,必须要全局
    const res4 = message1.matchAll(re2)
    console.log(res4)//RegExpStringIterator {}
    console.log(res4.next())//{value: Array(1), done: false}
    console.log(res4.next())//{value: Array(1), done: false}
    console.log(res4.next())//{value: Array(1), done: false}
    console.log(res4.next())//{value: Array(1), done: false}
    console.log(res4.next())//{value: undefined, done: true}
    //1.5 replace/replacAll 字符串方法:对字符串做替换
    const res5 = message1.replace(re2,"cba")
    console.log(res5)//fdcba123 fcba31231 dfcba1312 Aacba1231
    const res6 = message1.replaceAll(re2,"bbb")
    console.log(res6)//fdbbb123 fbbb31231 dfbbb1312 Aabbb1231
    //1.6 split :切割
    const res7 = message1.split(re2)
    console.log(res7)//(5) ['fd', '123 f', '31231 df', '1312 Aa', '1231']
    //1.7 search 方法:搜索结果
    const res8 = message1.search(re2)
    console.log(res8)// 2

修饰符flag的使用

◼ 常见的修饰符:
  flag    含义
   g      全部的,给我匹配全部的(global)
   i      忽略大小写(ignore)
   m      多行匹配(multiple)
◼ 需求:
    获取一个字符串中所有的abc;
    let message = "Hello ABC abc Abc ABc abC AAaBC"
    const re1 = /abc/ig
    const res1 = message.match(re1)
    console.log(res1)
    将一个字符串中的所有abc换成大写;
    const res2 = message.replace(re1,"ABC")
    console.log(res2)

规则–字符类(Character classes)

  ◼ 字符类(Character classes)是一个特殊的符号,匹配特定集中的任何符号。
    字符                      含义
    \d(“d” 来自 “digit”)    数字:从0 到9 的字符。(一个\d只匹配一个)
    \s(“s” 来自 “space”)    空格符号:包括空格,制表符\t,换行符\n和其他少数稀有字符,例如\v,\f和\r。
    \w(“w” 来自“word”)      “单字”字符:拉丁字母或数字或下划线_。
    .(点)                   点. 是一种特殊字符类,它与“除换行符之外的任何字符”匹配
  ◼ 反向类(Inverse classes)
    \D 非数字:除\d 以外的任何字符,例如字母。
    \S  非空格符号:除\s 以外的任何字符,例如字母。
    \W 非单字字符:除\w 以外的任何字符,   例如非拉丁字母或空格。

    //\d 匹配所有的数字
    const message = "fdaaa4 2222411d66da6d8da8a8d"
    const re1 = /\d+/ig
    const re2 = /\d/ig
    const rquest1 = message.match(re1)
    console.log(rquest1)//(7) ['4', '2222411', '66', '6', '8', '8', '8']
    console.log(message.match(re2))//(14) ['4', '2', '2', '2', '2', '4', '1', '1', '6', '6', '6', '8', '8', '8']
    //反向类
    const re3 = /\D/ig
    console.log(message.match(re3))//(14) ['f', 'd', 'a', 'a', 'a', ' ', 'd', 'd', 'a', 'd', 'd', 'a', 'a', 'd']

规则–锚点(Anchors)

◼ 符号^ 和符号$ 在正则表达式中具有特殊的意义,它们被称为“锚点”。
    符号^ 匹配文本开头;
    符号$ 匹配文本末尾;
  案例:
    const message = "aaa my name is hdc."
    // 字符串方法
    if (message.startsWith("my")){
      console.log("以my开头")
    }
    if(message.endsWith("hdc")){
      console.log("以hdc结尾")
    }
    // 用正则:锚点
    if(/^my/ig.test(message)){
      console.log("以my开头")
    }
    // 匹配.本身的时候要用转意\.不然.表示任意字符
    if(/hdc\.$/ig.test(message)){
      console.log("以hdc.结尾")
    }
    const re = /^webKing$/
    const re1 = /^web\w*g$/
    const info = "webddddddg"
    console.log(re.test(info))//false
    console.log(re1.test(info))//true
◼ 词边界(Word boundary)
    词边界\b 是一种检查,就像^ 和$ 一样,它会检查字符串中的位置是否是词边界。
    词边界测试\b 检查位置的一侧是否匹配\w,而另一侧则不匹配“\w”
◼ 在字符串Hello, Java! 中,以下位置对应于 \b:
◼ 匹配下面字符串中的时间:
    const message = "my aaanamebbb is hdc"
    // 需求:name.name必须是一个单独的词
    if(/name/ig.test(message)){
      console.log("有name")//有name
    }
    // 词边界
    if(/\bname\b/ig.test(message)){
      console.log("有name,有边界")//不打印
    }
    // 词边界应用
    const infos = "now time is 11:56 12:00 eat food, number is 123:456"
    const timeRe = /\b\d\d:\d\d\b/ig
    console.log(infos.match(timeRe))//(2) ['11:56', '12:00']

规则–转义字符串

◼ 如果要把特殊字符作为常规字符来使用,需要对其进行转义:
    只需要在它前面加个反斜杠;
◼ 常见的需要转义的字符:
    [] \ ^ $ . | ? * + ( )
    斜杠符号‘/’ 并不是一个特殊符号,但是在字面量正则表达式中也需要转义;
◼ 练习:匹配所有以.js或者jsx结尾的文件名
◼ 在webpack当中,匹配文件名时就是以这样的方式。
    //定义正则规则
    const re = /./ig
    const message = "abc.hdc"
    const request = message.match(re)
    console.log(request)//(7) ['a', 'b', 'c', '.', 'h', 'd', 'c']
    const re1 = /\./ig
    const request1 = message.match(re1)
    console.log(request1)//['.']

    // 特殊:/
    // 转义
    const re2 = /\//
    // 练习:匹配所有以.js或者.jsx结尾的文件
    const arrs= ["abc.html","bbc.css","hhh.js","eee.JS","rrr.jS","iii.jsx","ooo.Jsx"]
    const re3 = /\.jsx?$/ig
    const request2 = arrs.filter((arr)=>{return re3.test(arr)})
    console.log(request2)

集合(Sets)和范围(Ranges)

  ◼ 有时候我们只要选择多个匹配字符的其中之一就可以:
      在方括号[…] 中的几个字符或者字符类意味着“搜索给定的字符中的任意一个”;
  ◼ 集合(Sets)
      比如说,[eao] 意味着查找在3 个字符‘a’、‘e’ 或者`‘o’ 中的任意一个;
  ◼ 范围(Ranges)
      方括号也可以包含字符范围;
      比如说,[a-z] 会匹配从a 到z 范围内的字母,[0-5] 表示从 0 到5 的数字;
      [0-9A-F] 表示两个范围:它搜索一个字符,满足数字 0 到9 或字母A 到F;
      \d ——和[0-9] 相同;
      \w ——和[a-zA-Z0-9_] 相同;
  ◼ 案例:匹配手机号码
  ◼ 排除范围:除了普通的范围匹配,还有类似[^…] 的“排除”范围匹配;
    // 手机号规则:以1开头 1[3-9]
    //集合
    const phoneStarts = ["123","134","332","112","151","333","141"]
    const phoneStartsRe = /^1[3-9]\d/ig
    const filterPhone = phoneStarts.filter( phoneStart=>{return phoneStartsRe.test(phoneStart)})
    console.log(filterPhone)//(3) ['134', '151', '141']
    // 手机号匹配
    const phoneRe = /^1[3-9]\d{9}$/ig
    const phoneNumber = ["!312321321","151531531","15478890876"]
    const filterPhone1 = phoneNumber.filter(phone=>{return phoneRe.test(phone)})
    console.log(filterPhone1)//['15478890876']
    // 排除范围
    //\d ->[0-9]
    //\D =>[^0-9]

贪婪(Greedy)和惰性(lazy)模式

◼ 如果我们有这样一个需求:匹配下面字符串中所有使用《》包裹的内容
◼ 默认情况下的匹配规则是查找到匹配的内容后,会继续向后查找,一直找到最后一个匹配的内容
    这种匹配的方式,我们称之为贪婪模式(Greedy)
◼ 懒惰模式中的量词与贪婪模式中的是相反的。
    只要获取到对应的内容后,就不再继续向后匹配;
    我们可以在量词后面再加一个问号‘?’ 来启用它;
    所以匹配模式变为*? 或+?,甚至将'?' 变为??
    / 贪婪模式/惰性模式
    const message = "我最喜欢的两本书:《黄金时代》和《沉默的大多数》、《我是白小飞》"
    const  nameRe = /《.+》/ig
    const request1 = message.match(nameRe)
    console.log(request1)// 贪婪模式的结果:《黄金时代》和《沉默的大多数》、《我是白小飞》

    // 使用惰性模式
    const nameRe1 = /《.+?》/ig
    const request2 = message.match(nameRe1)
    console.log(request2)//(3) ['《黄金时代》', '《沉默的大多数》', '《我是白小飞》']

捕获组(capturing group)

◼ 模式的一部分可以用括号括起来(...),这称为“捕获组(capturing group)”。
◼ 这有两个作用:
     它允许将匹配的一部分作为结果数组中的单独项;
     它将括号视为一个整体;
◼ 方法str.match(regexp),如果regexp 没有 g 标志,将查找第一个匹配并将它作为一个数组返回:
     在索引0 处:完全匹配。
     在索引1 处:第一个括号的内容。
     在索引2 处:第二个括号的内容。
     …等等…
◼ 案例:匹配到HTML标签,并且获取其中的内容
    // 捕获组
    const nameRe2 = /(《)(.+?)(》)/ig
    const request3 = message.matchAll(nameRe2)
    // console.log(request3)是一个迭代器
    for (const item of request3){
      console.log(item[2])
    }
    // 将一个捕获组作为整体
    const info = "asdsadsabcabcadsabcadasdaabcsdassabcabcadsad"
    //需求:捕获连续两个abc abcabc
    const infoRe = /(abc){2}/ig
    const infoRequest = info.match(infoRe)
    console.log(infoRequest)//(2) ['abcabc', 'abcabc']
    案例:
    const str = "<div>title</div>"
    const strRe = /<(.+?)>/
    const request5 = str.match(strRe)
    console.log(request5[1])

捕获组的补充

◼ 命名组:
    用数字记录组很困难。
    对于更复杂的模式,计算括号很不方便。我们有一个更好的选择:给括号起个名字。
    这是通过在开始括号之后立即放置?<name> 来完成的。
    const result = message.match(/(?<hdc>hel)ol/i)
◼ 非捕获组:
    有时我们需要括号才能正确应用量词,但我们不希望它们的内容出现在结果中。
    可以通过在开头添加?: 来排除组。
    const result = message.match(/(?<hdc>hel)(?:ol){2}/i)
◼ or是正则表达式中的一个术语,实际上是一个简单的“或”。
    在正则表达式中,它用竖线| 表示;
    通常会和捕获组一起来使用,在其中表示多个值;
  const info = "asdsadsabcabccbaadsabcadasdanbaabcsdassabcabcadsad"
  //需求:捕获连续两个abc abcabc
  const infoRe = /(abc|cba|nba){2}/ig
  const infoRequest = info.match(infoRe)
  console.log(infoRequest)//(3) ['abcabc', 'nbaabc', 'abcabc']
posted @ 2024-10-22 11:35  韩德才  阅读(29)  评论(0编辑  收藏  举报