JS正则表达式相关的部分常用函数和属性

171 阅读6分钟

1.RegExp对象的函数和属性

1.1 基本属性(参考自W3school)

修饰符描述
i执行对大小写不敏感的匹配
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)
m执行多行匹配
属性描述是否只读
globalRegExp 对象是否具有标志 g只读
ignoreCaseRegExp 对象是否具有标志 i只读
multilineRegExp 对象是否具有标志 m只读
source正则表达式的源文本,不包括正则表达式直接量使用的定界符,也不包括修饰符 gim只读
lastIndex一个整数,标示开始下一次匹配的字符位置非只读属性,可以修改
// 说明:两种创建正则对象的写法是等价写法,区别在于使用构造函数创建正则对象可以传递参数,而简写方式不可以。后文中的案例都会使用简写方式
const regExp = new RegExp('^hello$','gim')
const newRegExp = /^hello$/gim
console.log(regExp)    // 输出 /^hello$/gim
console.log(newRegExp) // 输出 /^hello$/gim
// 简单输出测试RegExp对象的基本属性
const regExp = /^hello$/gim
console.log(regExp)             // 输出 /^hello$/gim
console.log(regExp.global)      // 输出 true
console.log(regExp.ignoreCase)  // 输出 true
console.log(regExp.multiline)   // 输出 true
console.log(regExp.source)      // 输出 "^hello$"
console.log(regExp.lastIndex)   // 输出 0

1.2 test函数

  • 可以用来校验某个字符串是否符合某个格式
  • 返回一个boolean
// 校验一个字符串是否为11位合法电话号码
const regExp = /^[1][3,4,5,7,8][0-9]{9}$/
const phone = '15889912345'
console.log(regExp.test(phone)) // 输出 true 	

1.3 exec函数

  • 可以用来在字符串中截取特定匹配项
  • 如果字符串中有符合正则表达式的匹配项,返回一个匹配数组,无则返回null
  • 正则表达式不加g修饰符,进行懒匹配,第一次有匹配项的时候立刻返回一个匹配数组
  • 正则条件加g修饰符,进行全局匹配,可以通过循环调用exec函数,逐次返回各个匹配数组
// 懒匹配,截取一句话里的第一个英文单词
const regExp = /[a-z]+/i
const words = 'Hello World'
const result = regExp.exec(words) 
console.log(result)    // 输出 ["Hello", index: 0, input: "Hello World"]
console.log(result[0]) // 输出 Hello
匹配数组的属性描述
index匹配字符串的在原字符串中的起始下标
input原字符串
result[0]原字符串中第一个匹配整个正则表达式的字符串
// 全局匹配,截取一句话里的每个英文单词
const regExp = /[a-z]+/ig
const words = 'Hello World'
let result = null 
while ((result = regExp.exec(words)) != null) {
    console.log('result:', result)    
    console.log(`result[0]: ${result[0]}`)
    console.log(`regExp.lastIndex: ${regExp.lastIndex}`)
}
console.log(`重置后的regExp.lastIndex: ${regExp.lastIndex}`)

// 控制台依次输出
result: ["Hello", index: 0, input: "Hello World"]
result[0]: Hello
regExp.lastIndex: 5
result: ["World", index: 6, input: "Hello World"]
result[0]: World
regExp.lastIndex: 11
重置后的regExp.lastIndex: 0

结论:只有在全局匹配的模式下,lastIndex会在exec函数的执行后变化,记录着原字符串中开始下一次匹配的字符下标,直到exec函数无匹配项,返回null的时候,lastIndex才会自动重置为0

1.4 RegExp.$n属性(只读)

  • 目标匹配项的正则子表达式需要加括号,代表一个分组
  • 在每次exec函数返回的匹配数组中,可以获取对应匹配项
  • n的范围是[1,9]的正整数,即RegExp.$1直到RegExp.$9
// 截取时间字符串里的时分秒信息,分组1是([0-1]?[0-9]|2[0-3]),用来截取小时,分组2是([0-5][0-9]),用来截取分钟,分组3是([0-5][0-9]),用来截取秒
const regExp = /([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])/
const dateTime = '2018-08-03 23:15:50'
const result = regExp.exec(dateTime) 
console.log(result) // 输出 ["23:15:50", "23", "15", "50", index: 11, input: "2018-08-03 23:15:50"]
console.log(result[1]) // 输出 23
console.log(result[2]) // 输出 15
console.log(result[3]) // 输出 50
console.log(RegExp.$1) // 输出 23
console.log(RegExp.$2) // 输出 15
console.log(RegExp.$3) // 输出 50

输出数据说明:

result属性RegExp属性描述
result[1]RegExp.$1匹配正则表达式的第一个分组的字符串
result[2]RegExp.$2匹配正则表达式的第二个分组的字符串
result[3]RegExp.$3匹配正则表达式的第三个分组的字符串

正则表达式说明:

表达式作用
( )1.在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰
2.取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到

结论:由括号表达式的第2点作用和以上例子可以知道,在exec函数执行结果有返回匹配数组的情况下,正则表达式的每个分组都对应一个匹配数组中的匹配子串,可以被单独取得。 (1)当n等于[1,9]的任一正整数时,RegExp.$n对应匹配数组中的第n个匹配子项,等价于result[n] (2)当n>9的时候,只能通过result[n]来取匹配子项

2.String对象的函数

2.1 replace函数

  • 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
  • 函数的第二个参数replacement可以为字符串或函数(函数必须有返回值,否则替换字符串会变成undefined) 中的 $字符具有特定的含义。如下表所示,它说明从模式匹配得到的字符串将用于替换
字符替换文本
$1、$2、...、$99与 regexp 中的第1 到第 99 个子表达式相匹配的文本
$&与 regexp 相匹配的子串
$` 位于匹配子串左侧的文本
$'位于匹配子串右侧的文本
$$直接量符号,代表转义出$字符

基本用法:

// 把一句话中的非法关键字替换为"**"
let words = '他是一个大笨蛋!'
let result = words.replace(/笨蛋/g, '**')
console.log(result) // 输出 他是一个大**!

// 在单词"friend"左边插入单词"best"
words = 'Hello, my friend'
result = words.replace(/friend/, 'best $&')
console.log(result) // 输出 Hello, my best friend

// $`和$'没想到什么实际应用的场景
words = 'Hello World'
result = words.replace(/Hello\s/, `$'`)
console.log(result)  // 输出 WorldWorld
result = words.replace(/\sWorld/, '$`')
console.log(result)  // 输出 HelloHello

进阶用法:

// 驼峰命名转横杠命名
const camelParamName = 'myCompanyName'
let result = camelParamName.replace(/([A-Z])/g, '-$1').toLowerCase()
console.log(result) // 输出 my-company-name

result = camelParamName.replace(/[A-Z]/g, '-$&').toLowerCase()
console.log(result) // 输出 my-company-name

结论: (1)正则里有用括号分组才可以使用$1、$2、...、$99去取匹配文本 (2)$1$&在非全局模式会获得第一个匹配的文本,在全局模式会获得所有匹配的文本(在此例子中会匹配myCompanyName中的C和N字符)

// 横杠命名转驼峰命名
const dashParamName = 'my-company-name'
const result = dashParamName.replace(/-[a-z]/g, matched => {
    console.log(`matched: ${matched}`)
    return matched[1].toUpperCase()
})
console.log(`result: ${result}`)

// 控制台依次输出
matched: -c
matched: -n
result: myCompanyName

执行顺序说明(个人理解): (1)执行replace函数 (2)执行第一次正则匹配,获得匹配子串'-c' (3)执行第一次replacement回调函数,matched='-c',matched[1]='c',matched[1].toUpperCase() = 'C'然后返回'C' (4)拼接字符串'myC' (5)执行第二次正则匹配,获得匹配子串'-n' (6)执行第二次replacement回调函数,matched='-n',matched[1]='n',matched[1].toUpperCase() = 'N'然后返回'N' (7)拼接字符串'myCompanyN' (8)执行第三次正则匹配,无匹配项,返回null,停止匹配 (9)拼接并返回字符串'myCompanyName',结束replace函数

结论:全局模式下,replacement函数执行次数等于正则匹配子串个数,matched就是匹配子串,每次进入replacement函数,对匹配子串进行处理后再返回(注意:replacement函数必须有返回值,否则替换文本会变成undefinedreplace函数会返回一个经过以上执行顺序处理后的新字符串)

2.2 split函数

  • 把一个字符串分割成字符串数组 |参数 |描述| |---- |:----| |separator|必需。字符串或正则表达式,从该参数指定的地方分割 stringObject| |howmany |可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度|
// 把一个日期时分秒字符串切割成年、月、日、时、分、秒数组
const regExp = /-|\s|:/
const dateTime = '2018-08-06 16:20:00'
let result = dateTime.split(regExp)
console.log(result) // 输出 ["2018", "08", "06", "16", "20", "00"]

// 把一个日期时分秒字符串切割成年、月、日数组
result = dateTime.split(regExp, 3)
console.log(result) // 输出 ["2018", "08", "06"]

2.3 match函数

  • 可以用来在字符串中截取特定匹配项
  • 正则表达式不加g修饰符,进行懒匹配,第一次有匹配项的时候立刻返回一个匹配数组,无则返回null
  • 正则条件加g修饰符,进行全局匹配,有匹配返回所有匹配子串合并后的数组,无则返回null
// 懒匹配,获取算式中的第一个数字
let regExp = /\d/
const equation = '1+2=3'
let result = equation.match(regExp)
console.log(result) // 输出 ["1", index: 0, input: "1+2=3"]

// 全局匹配,获取算式中的所有数字
regExp = /\d/g
result = equation.match(regExp)
console.log(result) // 输出 ["1", "2", "3"]

结论: (1) 非全局匹配模式下,match函数的返回值和exec函数的一样 (2) 全局匹配模式下,match函数只返回所有匹配子串合并后的数组,不会返回匹配项的详细信息。因此,如果需要每个匹配项的详细信息,就需要循环调用exec函数