红宝书 📒 5.2 基本引用类型- RegExp

430 阅读2分钟

「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

RegExp

前言

正则表达式是很重要的。几乎每一门语言都会去支持正则表达式,ECMAScript是通过RegExp类型来支持正则的。正则其实很常用,比如我们的路由是怎么去相应的匹配页面、在一段文字中提取我想要的文字等等都需要去使用正则。

正则匹配

  • 这个正则表达式的pattern(模式)可以是简单活复杂的正则表达式(字符串、限定符、分组、向前查找反向引用)

  • 正则表达式可以带0~多个flags(标记)来控制正则表达式的行为。

匹配模式标记

  • g :全局模式,表示查找字符的全部内容
  • i :不区分大小写,表示查找匹配时忽略pattern和字符串的大小写
  • m :多行模式,表示查找到一行文本末尾会继续查找
  • y :粘附模式,表示只查找从lastIndex开始及之后的字符串。
  • u :Unicode模式 ,启用Unicode匹配
  • s :doAll模式,表示元字符,匹配任何字符(包括 \n \r) 标记也可以组合使用
let pattern = /at/gi

创建正则对象可以用字面量形式或者RegExp构造函数形式

字面量形式

也就是我们上面定义的这种

let pattern = /at/gi

元字符

\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。如\n匹配换行符
^ 匹配输入字符串的开始位置
$ 匹配输入字符串的结束位置
* 匹配前面的子表达式0次或多次
+ 匹配前面的子表达式1次或多次
?匹配前面的子表达式0次或1次
. 匹配换行符之外的任何单个字符
x|y 匹配x或y
[xyz] 匹配所包含的任意一个字符
[^xyz] 匹配未包含的任意一个字符
[a-z] 匹配a-z的任意一个小写字母
\d  匹配一个数字字符 等价于 [0-9]
\D  匹配一个非数字字符 等价于[^0-9]


let pa = /[bc]at/i          匹配第一个"bat""cat" ,忽略大小写
let pa = /\[bc]at/i         匹配第一个"[bc]at" 忽略大小写
let pa = /.at/gi            匹配所有以"at"结尾的三字符组合,忽略大小写
let pa = /\.at/gi           匹配所有".at"忽略大小写

要想使用元字符,必须进行转义。

RegExp构造函数

这种方式,传入的两个参数都是字符串

let pat = new RegExp("[bc]at","i")

元字符需要二次转意 \转义为 \\,如 \n \\n

字面量模式                    对应的字符串

/\[bc\]at/                   "\\[bc\\]at"
/\.at\                       "\\.at"
/name\/age/                  "name\\/age"
/\d.\d{1,2}/                 "\\d.\\d{1,2}"  
/\w\\hello\\123/             "\\w\\\\hello\\\\123"

5.2.1 RegExp实例属性

这些属性可以全面了解正则表达式的信息,但是实际开发并不常用。比如我不需要知道正则表达式中是否使用了g。

  • global:布尔值,表示是否设置了 g 标记。
  • ignoreCase:布尔值,表示是否设置了 i 标记。
  • unicode:布尔值,表示是否设置了 u 标记。
  • sticky:布尔值,表示是否设置了 y 标记。
  • lastIndex:整数,表示在源字符串中下一次搜索的开始位置,始终从 0 开始。
  • multiline:布尔值,表示是否设置了 m 标记。
  • dotAll:布尔值,表示是否设置了 s 标记。
  • source:正则表达式的字面量字符串(不是传给构造函数的模式字符串),没有开头和结尾的 斜杠。
  • flags:正则表达式的标记字符串。始终以字面量而非传入构造函数的字符串模式形式返回(没有前后斜杠)。

5.2.2 RegExp实例方法

这里我们介绍两个方法 exec() test()

exec()

参数:要应用模式的字符串

需求:内部的匹配项"and baby",内部的 "and dad" 或 "and dad and bay"

let txt ="mom and dad and baby"
// 注意空格
let pattern = /mom( and dad( and baby)?)?/gi
let match = pattern.exec(txt)
// ['mom and dad and baby', ' and dad and baby', ' and baby', index: 0, input: 'mom and dad and baby', groups: undefined]
  • match[0]: 要查找的字符串
  • match[1]: 匹配的第一个字符串
  • match[2]: 匹配的第二个字符串(当匹配条件是多个的时候)
  • match["input"]: 要查找的字符串
  • match["index"]: 匹配到字符串的索引

思考一下 🤔

为什么这个数组 有字符串 还有键值对? 我这么定义是错的?那这还是数组吗?

let arr = [1,2,"test":11]  // Uncaught SyntaxError: Unexpected token ':'

没错这是一个普通数组,已分配了某些其他属性。由于数组是对象,因此它们除了通常的数字索引外还可以具有任意键值对,尽管您几乎永远都不会在普通的干净代码中看到这一点(正则表达式匹配是唯一可以想到的数组对象所在的位置其他非标准属性)。 其实可以像下面的方法定义,就得到了类似的数组。

let arr = [1]
arr.input = "test"  // [1, 2, input: 'test']

全局标记 g

全局匹配究竟是什么意思?即使exec(),使用了全局匹配,返回的结果还是相同的啊?

let text = "cat, bat, sat, fat"


let nogpattern = /.at/
// ['cat', index: 0, input: 'cat, bat, sat, fat', groups: undefined]
nopattern.exec(text)  


// ['cat', index: 0, input: 'cat, bat, sat, fat', groups: undefined]
let havegpattern = /.at/g
havegpattern.exec(text)

这是因为需要再次调用exec(),它才会再向下寻找。这时候才能体现出 g的作用

没使用 g

let text = "cat, bat, sat, fat"
let nog = /.at/
nog.exec(text) // ['cat', index: 0, input: 'cat, bat, sat, fat', groups: undefined]
console.log(nog.lastIndex)   // 0 最后匹配到的索引
nog.exec(text) //['cat', index: 0, input: 'cat, bat, sat, fat', groups: undefined]
console.log(nog.lastIndex)   // 0

使用 g

let text = "cat, bat, sat, fat"
let haveg = /.at/g  // ['cat', index: 0, input: 'cat, bat, sat, fat', groups: undefined]
console.log(haveg.lastIndex)    // 0
haveg.exec(text)  // ['bat', index: 5, input: 'cat, bat, sat, fat', groups: undefined]
console.log(haveg.lastIndex)   // 8

粘附标记y

只查找从lastIndex开始和以后的字符串

let text = "_aa_a"
let pattern = /_a+/y
pattern.exec(text)  // ['_aa', index: 0, input: '_aa_a', groups: undefined]

y

但是 我们想匹配aa 或 a 返回的就是null。因为从lastIndex(0)开始寻找 。它找的字符串是 ["_","_a","_aa","_aa_","_aa_a"]也就是从下标为0开始的所有排列租户,

let text = "_aa_a"
let pattern = /a+/y    
pattern.exec(text)           //null
console.log(text.lastIndex) // 0

如果重新定义 patternlastIndex 则就可以找到

pattern.lastIndex = 1
pattern.exec(text)           // pattern.exec(text)           //null

g

而如果是g,则找的是["_","_a","_aa","_aa_","_aa_a","a","aa","aa_","aa_a","a","a_","a_a","_","_a","a"]也就是所有可能出现的排列组合。

test()

这个方法用于校验,模式是否匹配。返回true 或 false

let a = "_aa_a"
let pattern = /_a+/g
pattern.test(a)             // true

toLocaleString() 和 toString()

let pattern = new RegExp("\\[bc\\]at","gi");
pattern.toString()          //          '/\[bc\]at/gi'
pattern.toLocaleString()    //          '/\[bc\]at/gi'

valueof()

返回正则表达式本身

5.2.3 RegExp 构造函数属性

通过以下属性可以提取出与exec()和test()执行的操作相关信息

  • input 缩写 $_ 最后搜索的字符串
  • lastMatch 缩写 $& 最后匹配的文本
  • lastParen 缩写 $+ 最后匹配的捕获组
  • leftContext 缩写 $` input字符串中出现在lastMatch前面的文本
  • rightContext 缩写 $' input字符串中出现在lastMatch后面的文本
let text = "this has been a short summer";
let pattern = /(.)hort/g
if(pattern.test(text)){
    console.log(RegExp.input);         // this has been a short summerh'n'm'n
    console.log(RegExp.$_);           // 同上
    console.log(RegExp.leftContext);    // tis has been a
    console.log(RegExp["$`"])           // 同上
    console.log(RegExp.rightContext);   // summer
    console.log(RegExp.lastMatch);     // short
    console.log(RegExp.lastParen);     // s
}