如何在 Javascript 中优雅的使用正则表达式
写一篇文章来总结一下如何在 JS
中使用正则表达式。
RegExp 类型
JS 中可以通过 let expression = / pattern / flags
创建一个正则表达式。这个表达式是通过 RegExp 这个引用类型来支持的。
其中, parttern
可以是任何简单或复杂的正则表达式。 每一个正则表达式可以带有一个或多个 flags
, 用以表明表达式的行为。
g
: 表示全局(global)模式, 即模式将被应用与所有字符串, 而非在发现第一个匹配项立即停止。i
: 表示不区分大小写m
: 表示多行模式
与其他语言类似, 模式中使用的所有元字符都必须转义,正则表达式中的元字符包括: ( { [ \ ^ $ | ] ? * + . } )
还可以使用 RegExp 的构造函数来声明正则表达式
let pattern1 = /[bc]at/i // 匹配第一个 bat 或者 cat 不区分大小写
let pattern2 = /\[bc\]at/i // 匹配第一个 [bc]at 不区分大小写
等价于
let pattern1 = new RegExp('[bc]at', 'i') // 匹配第一个 bat 或者 cat 不区分大小写
let pattern2 = new RegExp('\\[bc\\]at', 'i') // 匹配第一个 [bc]at 不区分大小写
由于 RegExp 的参数是字符串,所以所有元字符需要进行双重转义
RegExp 的实例方法
RegExp 对象的主要方法是 exec()
exec()
方法接受一个参数(string), 返回包含第一个匹配项信息的数组,在没有匹配项的情况下返回 null
。
返回的数组实例包含额外的 index
和 input
属性。
index
表示匹配项在字符串中的位置, input
表示应用正则表达式的字符串。
举个栗子
栗子中的分组语法可下滑至分组章节查看。
let text = 'mom and dad and baby'
let pattern = /mom( and dad (and baby)?)?/gi
const list = pattern.exec(text)
console.log(list.index) // 0
console.log(list.input) // 'mom and dad and baby'
console.log(list[0]) // 'mom and dad and baby'
console.log(list[1]) // ' and dad and baby'
console.log(list[2]) // 'and baby'
RegExp 对象上面的第二个方法是 test()
test()
方法接受一个字符串参数, 正则与参数相匹配返回 true
不匹配返回 false
元字符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母数字或下滑线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
/\ba\w*b/.test(/** 匹配以字母 a 开头的单词 */)
重复
代码 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n, m} | 重复n到m次 |
字符串
[aeiou]
匹配任何一个英文元音字母, [.?!]
匹配标点符号 (.或?或!), [0-9]
等价于 /d
匹配一位数字, [a-z0-9A-Z]
等价于 /w
(只考虑英文的话)
分枝条件
/0\d{2}-\d{8}|0\d{3}-\d{7}/
这个表达式能够匹配两种以连字号分隔的电话号码: 一种是三位区号,八位本地号(如: 010-12345678),一种是四位区号,七位本地号(如0376-2233445)
/\(0\d{2}\)[-]?\d{8}|0\d{2}[-]?\d{8}/
这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔
分组
我们可以用小括号指定子表达式(也叫做分组), 这样我们可以指定多个字符(子表达式)的重复次数
(\d{1,3}\.){3}\d{1,3}
是一个简单的 IP 地址皮票表达式
反义
代码 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下滑线或汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词的开始或结束的位置 |
[^x] | 匹配除了 x 以外的任何字符 |
[^aeiou] | 除了 aeiou 这几个字母以外的任意字符 |
字符串(string
)中可以使用正则的方法
String
类型定义了几个用于在字符串中匹配正则的方法, 第一个是 match()
match()
方法跟 exec()
的结果相同。这个方法只接受一个参数(一个正则表达式)。
const text = 'cat, bat, sat, fat, hat'
const pattern = /.at/
const matches = text.match(pattern)
console.log(matches.index) // 0
console.log(matches[0]) // cat
const text = 'cat, bat, sat, fat, hat'
const pattern = /.at/g
const matches = text.match(pattern)
console.log(matches) // ['cat', 'bat', 'sat', 'fat', 'hat']
第二个方法是 replace
方法, 这个方法接受两个参数, 第一个参数是正则或者一个字符串, 第二个参数可以是字符串或者函数。如果第一个参数是字符串, 那么只会替换第一个自字符串。如果需要替换所有匹配的自字符串需要提供正则表达式,且指定全局(g
)标志。
const text = 'cat, bat, sat, fat'
const result = text.replace('at', 'ond')
console.log(result) // cond, bat, sat, fat
const text = 'cat, bat, sat, fat'
const result = text.replace(/at/g, 'ond')
console.log(result) // cond, bond, sond, fond
replace()
方法的第二个参数也可以是一个函数, 在只有一个匹配项的情况下, 会向这个函数传递三个参数, 匹配项, 匹配项在字符串的位置和原始字符串。如果有多个匹配项,那么参数列表为,第一个匹配项, 第二个匹配项, ....., 最后两项仍为匹配项在字符串的位置和原始字符串。这个函数的返回值应为字符串, 表示被替换的匹配项。
面试题集锦
如何使用正则实现 trim()?
const trim = (str) => str.replace(/^\s+|\s+$/g, "")
用正则实现一个简单的 function 转换功能
// 将普通的函数声明方式,转化为 es6 语法的匿名函数声明
// 不用考虑闭包、特殊符号、函数上下文等边际情况
// 入参格式参考1:
const inputFuncStr = "function a () { console.log('transfer') }";
// 出参格式参考1:
const outputFuncStr = "const a = () => { console.log('transfer') }";
// case1
const inputFuncStrCase1 = "function() {}"
// expect1
const outputFuncStrExpect1 = "() => {}";
// case2
const inputFuncStrCase2 = "const a = function() {}"
// expect2
const outputFuncStrExpect2 = "const a = () => {}";
// case3
const inputFuncStrCase3 = "function test_1() {}"
// expect3
const outputFuncStrExpect3 = "const test_1 = () => {}";
export function transfer(normalText) {
let reg = /(function)([\w ]*)(\([\w ,]*\))/
return normalText
.replace(reg, (match, s1, s2, s3) => {
if (s2) {
return `const${s2}= ${s3} =>`
} else {
return `${s3} =>`
}
})
}