正则表达式的使用

213 阅读5分钟

1. 正则对象 RegExp 上的常见属性和方法

1.1 RegExp 的属性

1.1.1 只读属性: RegExp.prototype.global , RegExp.prototype.ignoreCase , RegExp.prototype.multiline

值为 truefalse, 表示该正则表达式是否使用了对应的修饰符

  • global 描述是否使用 g 修饰符
  • ignoreCase 描述是否使用 i 修饰符
  • multiline 描述是否使用 m 修饰符

1.1.2 regExpObj.lastIndex(可读可修改)

  • 只有存在修饰符 g 的时候才起作用(否则无论 lastIndex 的值是多少, 都默认为 0)
    let reg = /\d+/
    reg.lastIndex = 4
    
    console.log(reg.lastIndex) //=> 4
    reg.exec("123a456") //=> ["123", index: 0, input: "123a456", groups: undefined]
    // 即使更改了 lastIndex 的值, 其在匹配的时候依然会从 0 开始
    
  • 表示下一次匹配字符串时, 从字符串开始的下标 ( 无论是否和刚才匹配的是同一个字符串 )
  • 当匹配字符串失败时, 其值又会变为 0
    let reg = /\dabc/g
    let str = "1abc2abc3abc"
    
    console.log(reg.lastIndex) //=> 0 表示下一次匹配从字符串下标为 0 的位置开始
    reg.exec(str) //=> ["1abc", index: 0, input: "1abc2abc3abc", groups: undefined]
    
    console.log(reg.lastIndex) //=> 4 表示下一次匹配从字符串下标为 4 的位置开始
    reg.exec(str) //=> ["2abc", index: 4, input: "1abc2abc3abc", groups: undefined]
    
    // 更换正则所匹配的字符串, 依然会从 lastIndex 的位置开始匹配
    console.log(reg.lastIndex) //=> 8 表示下一次匹配从字符串下标为 8 的位置开始
    reg.exec("0abc12345abc") //=> ["5abc", index: 8, input: "0abc12345abc", groups: undefined]
    

1.2 RegExp 的方法

1.2.1 RegExp.prototype.test(str)

尝试用正则对象对字符串 str 进行匹配, 若能够匹配则返回 true, 若不能匹配就返回 false

如果正则表达式中有修饰符 g, 则其还有更改 lastIndex 的能力

/\d+/.test("12345") //=> true
/\d+/g.test("abcde") //=> false

let reg = /\d+/g
console.log(reg.lastIndex) //=> 0
reg.test("12abc") //=> true
console.log(reg.lastIndex) //=> 2

1.2.2 RegExp.prototype.exec(str)

尝试用正则表达式对字符串 str 进行匹配, 若不能匹配则返回 null,

若能匹配, 则返回一个数组

  • 下标 0 : 表示正则表达式的匹配结果
  • 下标 1, 2, 3... : 表示捕获所得的结果
  • 属性 index : 表示正则表达式所匹配的子字符串在原字符串 str 中的起始下标
  • 属性 input : 表示用于匹配的源字符串 str
  • 属性 groups : 具名捕获组
    • 最基本的捕获的应该是 (x) , 表示捕获匹配正则表达式 x 规则的字符串
    • 但是应用具名捕获组: (?<name>x), 在捕获了对应字符串的前提下, 还会将捕获的字符串以 value 的形式存储在 key 为自主命名为 namegroups 对象中
let reg = /(?<integerPart>\d+)\.(?<decimalPart>\d+)/
let str = "123.456"

let array = reg.exec(str)

console.log(array) 
//=> ["123.456", "123", "456",
// index: 0, input: "123.456", groups: {integerPart: "123", decimalPart: "456"}]
/** 
 * array[0] 为该正则匹配的结果, array[1, 2, 3...] 为分组匹配的结果
 * array.index 为正则表达式所匹配的字符串在源字符串中的起始下标
 * array.input 为正则表达式所匹配的源字符串
 * array.groups 为具名捕获组的对象, 存放了具名捕获的 key-value 键值对
 */

2. String 上的有关正则的常见方法

2.1 String.prototype.match(reg)

  • 如果传入的 reg 并不是 RegExp , 则会将其转换成 RegExp
  • 如果 reg 中没有 g 修饰符, 则其返回值和 RegExp.prototype.exec(str) 一样
  • 如果 reg 中含有 g 修饰符, 则返回所有正则匹配的结果所组成的数组(没有 index , inputgroups, 也没有捕获所得到的字符串), 无法匹配则返回 null
let str = "123.456"

str.match(/\d+/) //=> ["123", index: 0, input: "123.456", groups: undefined]
str.match("\\d+") //=> ["123", index: 0, input: "123.456", groups: undefined]
str.match(/\d+/g) //=> ["123", "456"]
str.match(/(\d+)\.(\d+)/g) //=> ["123.456"]

2.2 String.prototype.matchAll(reg)

弥补了 String.prototype.match(reg) 不能获取捕获内容的缺点

  • 如果传入的 reg 并不是 RegExp , 则会将其转换成 RegExp
  • 如果 reg 中没有 g 修饰符, 则会直接报错
const str = "String.prototype.matchAll(reg)"
const reg = /(\w+)/g
console.log(...str.matchAll(reg)) 
/* =>
 * ["String", "String", index: 0, input: "String.prototype.matchAll(reg)", groups: undefined] 
 * ["prototype", "prototype", index: 7, input: "String.prototype.matchAll(reg)", groups: undefined] 
 * ["matchAll", "matchAll", index: 17, input: "String.prototype.matchAll(reg)", groups: undefined] 
 * ["reg", "reg", index: 26, input: "String.prototype.matchAll(reg)", groups: undefined]
 * 每一个数组都相当于是一次 reg.exec(str) 的结果
 */

2.3 String.prototype.replace(reg|subStr, newSubStr|callbackFn)(并不改变源字符串)

简要说明: 匹配第一个参数, 并用第二个参数替换

  • 第一个参数: reg|subStr : 用于匹配被替换的字符串中的子字符串的正则表达式或子字符串
    • 正则表达式如果没有 g 修饰符, 则和 subStr 一样只替换第一个匹配到的字符串
      let str = "123abc123"
      
      let resStr0 = str.replace(/\d+/, "xxx") // 非全局匹配
      let resStr1 = str.replace(/\d+/g, "xxx") // 全局匹配
      let resStr2 = str.replace("123", "xxx")
      
      console.log(resStr0, resStr1, resStr2) //=> "xxxabc123", "xxxabcxxx", "xxxabc123"
      
  • 第二个参数: newSubStr|callbackFn : 对每次正则匹配的结果用 newSubStrcallbackFn 的返回值替换
    • newSubStr : 有特殊的用法(在字符串中插入特殊的变量名)
      • $n : 100 > n >= 1 , 表示从 1 开始的捕获

        let str = "123.456".replace(/(\d+)\.(\d+)/, "$2.$1")
        // 该正则匹配到的第一组捕获是 "123", 第二组捕获是 "456"
        console.log(str) //=> "456.123"
        
      • $<name> : 当有具名捕获组时, 可以用 $ + <名字> 来表示对应捕获的内容

        let str = "123.456"
        let reg = /(?<left>\d+)\.(?<right>\d+)/
        let newSubStr = "$<right>+$<left>"
        
        str.replace(reg, newSubStr) //=> "456+123"
        
      • $& : 表示当次正则匹配到的子字符串

        "123.456".replace(/\d+/g, "0$&") //=> "0123.0456"
        /**
         * 由于是全局匹配, 所以会进行多次匹配
         * 第一次正则匹配的结果是 "123", 所以 "$&" 代表的就是 "123"
         * 对于第一次匹配, 替换为 "0$&", 即替换为 "0123"
         * 第二次正则匹配的结果是 "456", 所以 "$&" 代表的就是 "456"
         * 对于第二次匹配, 替换为 "0$&", 即替换为 "0456"
         */
        
      • $` : $ + `(反引号) 表示当前所匹配的子串的左边的内容

        $' : $ + '(单引号) 表示当前所匹配的子串的右边的内容

        let str = "123.456".replace(/\./, "{left: $`, right: $'}")
        // 该正则匹配到字符串中的 ".", 并用 "{left: $`, right: $'}" 替换
        console.log(str) //=> "123{left: 123, right: 456}456"
        
    • callbackFn(subStr, $1, $2, ..., offset, str, groups) : 返回值替代这次正则表达式所匹配的子字符串
      • subStr : 表示当前正则匹配到的字符串
      • $1, $2, ... : 表示分组捕获到的字符串
      • offset : 表示当前所匹配到的子字符串在源字符串中的起始下标
      • str : 被匹配的源字符串
      • groups : 如果有具名捕获组的话, 就为对应具名捕获组对象, 否则为 undefined
      let str = "this is a string"
      let reg = /\b(\w{1})\w*\b/g
      str.replace(reg, (subStr, capture1, offset, str, groups) => {
        console.log(subStr, capture1, offset, str, groups)
        //=> "this", "t", 0, "this is a string", undefined
        //=> "is", "i", 5, "this is a string", undefined
        //=> "a", "a", 8, "this is a string", undefined
        //=> "string", "s", 10, "this is a string", undefined
        return subStr + "233"
      }) //=> "this233 is233 a233 string233"
      // 在上面的例子中, 一共进行了四次匹配替换, 最后返回最终结果
      

2.4 String.prototype.search(reg)

返回与对应正则表达式匹配的子字符串在源字符串中的起始下标, 如果无法匹配则返回 -1

let str = "abc123"
let reg = /\d+/
str.search(reg) //=> 3

2.5 String.prototype.split([separator[, limit]])(并不修改源字符串)

以传入的 separator 作为分隔符, 将分割后的结果存放到一个数组中并返回该数组, 如果有 limit, 则表示数组中的元素最多只能有 limit

  • separator : string | reg 如果没有匹配到, 则以整个字符串作为数组的一个元素; 如果 separator 为空字符串, 则源字符串中的每一个字符都是数组的一个元素
const str = "this is a string"
str.split() //=> ["this is a string"]
str.split(/\b/) //=> ["this", " ", "is", " ", "a", " ", "string"]
str.split(" ") //=> ["this", "is", "a", "string"]
str.split(" ", 2) //=> ["this", "is"]