正则 RegExp 前端使用手册
开篇
RegExp
(regular expression)正则,常常被用作处理 字符串 规则。使用场景包含两个角度:
- 校验,验证字符串是否符合某一规则,常见需求如:校验手机号格式 等;
- 匹配/捕获,匹配字符串中符合规则的内容,常见需求如:匹配模板表达式
{{}}
进行变量替换 等。
下面,我们从基础到实际应用,来学习和掌握前端正则的使用。
- 正则语法,
- 利用正则实现校验,
- 利用正则实现捕获,
- 工作上常见的正则使用场景。
一、正则语法
1. 创建正则表达式
- 构造函数方式:
正则拥有自己的构造函数,它接收 正则表达式 作为参数,返回一个正则实例,具备一些 RegExp
原型上的方法,下面验证输入内容是存在数字:
const reg = new RegExp('\\d+');
reg.test(123); // true
reg.test('abc'); // false
- 字面量方式:
通过两个斜杠 / /
的方式创建正则表达式,也具备 RegExp.prototype 上的方法,如:test、exec 等。
const reg = /\d+/;
reg.test(123); // true
reg.test('abc'); // false
- 二者区别:
- 构造函数方式,使用
\
时需要多加一个\
进行转译,否则会被当作普通字符使用;
2. 正则表达式的组成部分
上面我们了解了正则表达式的创建方式,在构造函数的参数、字面量两个斜杠之间的内容,就是编写正则表达式的地方。
正则表达式可以由两部分组成:元字符 和 修饰符。
1. 元字符:
是编写正则表达式的核心组成部分,用来定义字符串校验和匹配的规则。元字符可以划分为三类:
- 1)普通元字符:(完全字面量规则,含义代表本身)
正则定义为 /cegz/ 匹配的字符串就是 "cegz"
- 2)特殊元字符:(单个或多个组合在一起,来表示特殊含义的规则)
1. \ 转译字符,可以将普通字符转为有特殊含义的字符,也可将特殊含义字符转为普通字符
比如转移特殊字符 . 为普通字符:/2\.5/.test('2.5'); // true
2. ^ 指定开头规则使用的元字符,设置它后,将要求字符串的开头要和这里的元字符规则相匹配
console.log(/^\d/.test('2023abcd')); // true
3. $ 指定结尾规则使用的元字符,
console.log(/\d$/.test('abcd2023')); // true
当 ^$ 都加上,表示:字符串只能是和规则一样的内容,如验证 11 位手机号:/^1\d{10}$/
4. . 代表出了 `\n` 以外的任意字符,
5. \n 代表换行符
6. \d 一个集合,代表 0-9 之间的数字
7. \D 非 0-9 之间数的字,比如是字母(`大写都会与小写规则相反`)
8. \w 数字、字母、下划线(_) 中的任意一个字符
9. \W 即 非 \w
10. \s 代表一个空白字符(包含 空格、制表符、换行符)
11. \S 即 非 \s
12. \t 代表制表符(tab 键)
13. | 或的意思,如 x|y 表示 x 或者 y 其中一个
14. [] 其中的意思,如 [xyz] 表示 x、y、z 其中一个
15. [^] 取反的意思,如 [^xy] 表示除 x、y 以外的其他字符
16. [-] 指定范围,如 [a-z] 表示 a ~ z 范围之间的字符
17. () 代表分组,就是划分块,先整合这一块规则,它还有一个作用是`分组匹配`;
18. (?:) 只匹配,不捕获(具体含义后面 匹配 那里介绍)
- 3)量词元字符:(设置 普通/特殊 元字符出现的次数)
1. * 代表出现 0 ~ 多次
2. + 代表出现 1 ~ 多次
3. ? 代表出现 0 次或 1 次
4. {n} 用括号包裹,代表出现 n 次(n 是一个随意指定的数字)
5. {n,} 代表出现 n ~ 多次
6. {n,m} 代表出现 n ~ m 次
2. 修饰符:
修饰符有三个,用于指定正则的匹配策略:i、m、g
i(ignoreCase)
忽略单词大小写匹配;m(multiline)
忽略换行符,进行多行匹配;g(global)
全局匹配,匹配到所有满足条件的结果。
假设我们有一段字符串:
const str = `Google test google test google`;
在没有修饰符的情况下,正则只进行完全匹配,并返回匹配到的第一个信息:
console.log(str.match(/google/)); // ['google', index: 12]
如果加上 i
修饰符,正则会忽略大小写,匹配到 Google
:
console.log(str.match(/google/i)); // ['Google', index: 0]
修饰符也可以同时使用多个,下面匹配时忽略大小写,并匹配所有与正则有关的结果:
console.log(str.match(/google/gi)); // [ 'Google', 'google', 'google' ]
在了解了 元字符 和 修饰符 后,我们基于它们来编写正则表达式,来实现 校验 和 匹配。
二、利用正则实现校验
RegExp.test
通常用来进行正则校验,当实例化正则表达式后,正则就会拥有该方法,接收字符串作为参数进行匹配,若校验成功返回 true,否则返回 false。
我们编写正则验证手机号格式是否正确:以 1 开头,11 位数字
const reg = /^1\d{10}$/;
const iphone = '18933112266';
console.log(reg.test(iphone)); // true
三、利用正则实现捕获
实现对字符串内容进行捕获有两种方式:正则表达式 exec 捕获
和 字符串 match 捕获
,下面我们来看一看两者区别。
1. RegExp.exec
正则表达式提供了一个 exec
方法用于实现捕获,接收字符串作为参数,匹配结果为:
- 如果没有匹配到,结果为
null
; - 如果匹配到会返回一个数组,数组元素第一项为匹配的结果,第二项为匹配结果的起始位置(从 0 开始)。
let str = "abcd2023efgh0301";
let reg = /\d+/;
console.log(reg.exec(str));
// 输出:
[ '2023', index: 4, input: 'abcd2023efgh0301', groups: undefined ]
需要注意的是:执行 exec 只会匹配到一个符合规则的结果(惰性匹配
),默认只捕获第一个。
之所以惰性匹配是因为正则表达式有一个特殊属性 lastIndex
,标记正则要匹配的起始索引。默认这个值不会随调用 exec
的次数调整:
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
// 输出:
0
[ '2023', index: 4, input: 'abcd2023efgh0301', groups: undefined ]
0
如何解决正则惰性?直接修改 lastIndex 不可行,通过为正则表达式添加修饰符 g
实现全局匹配,在每次执行 exec
后会自动更新 lastIndex:
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
0
[ '2023', index: 4, input: 'abcd2023efgh0301', groups: undefined ]
8
不过这样每次需要手动调用 exec
才能匹配到下一个,我们编写 execAll
来帮助我们实现全部匹配:
~function () {
function execAll(str = '') {
if (!this.global) return this.exec(str); // 没有加 g,只捕获一次
let ary = [], res = null;
while (res = this.exec(str)) {
ary.push(res[0]);
}
return ary.length === 0 ? null : ary;
}
RegExp.prototype.execAll = execAll;
}();
console.log(reg.execAll(str));
// 输出:
[ '2023', '0301' ]
通常,我们若想实现全部捕获,会采用另一种捕获方式来代替 execAll
的实现:字符串 match 捕获
。
2. String.match
字符串原型上提供了 match
方法,接收正则表达式作为参数,它的匹配结果和 exec
很相似:
- 如果没有匹配到,结果为
null
; - 如果匹配到会返回一个数组,数组元素第一项为匹配的结果,第二项为匹配结果的起始位置(从 0 开始)。
let str = "abcd2023efgh0301";
let reg = /\d+/;
console.log(str.match(reg));
// 输出:
[ '2023', index: 4, input: 'abcd2023efgh0301', groups: undefined ]
如果你想实现全部匹配,只需在正则表达式上添加修饰符 g
即可:
let str = "abcd2023efgh0301";
let reg = /\d+/g;
console.log(str.match(reg));
// 输出:
[ '2023', '0301' ]
到这里你会发现,上面两种方式都是匹配结果,下面还有一种方式可以实现匹配,并且还可将匹配结果替换为新的值。(这在业务中经常会用到)
3. String.replace
字符串 replace
方法通常用作字符串替换,第一参数代表匹配的规则,可以是 字符串字面量 或 正则表达式;第二参数代表要替换的值,可以是 替换字符串值 或 处理函数,处理函数需要返回 替换字符串值。
- 如果第二参数为字符串,最简单的使用如下:
const str = "abcd 2023 abcd";
console.log(str.replace('2023', 'abcd')); // abcd abcd abcd
- 如果第二参数为函数,在匹配到结果时会被执行,需要返回一个字符串作为替换后的新值:
const str = "abcd 2023 abcd";
console.log(str.replace('2023', () => {
return 'abcd';
})); // abcd abcd abcd
既然是函数,自然会具备很高的灵活性,可以在函数体内编写复杂逻辑。重要的是,函数的参数可以为我们提供匹配信息。
如:第一参数 $1
代表匹配的结果,第二参数 $2
代表匹配结果的起始索引,第三参数 $3
代表原字符串。在某些场景下可以基于这些参数做一些特殊处理。
console.log(str.replace('2023', (...args) => {
console.log(args); // [ '2023', 5, 'abcd 2023 abcd' ]
return 'abcd';
}));
对于第一参数如果是 字符串字面量,只会匹配和替换第一个结果,即执行一次替换一次:
let str = "abcd 2023 abcd";
console.log(str.replace("abcd", "2023").replace("abcd", '2023'));
如果需要对字符串进行全局替换,第一参数可选用 正则表达式 来实现:
const str = "abcd 2023 abcd";
console.log(str.replace(/abcd/g, (...args) => {
console.log(args);
return '2023';
}));
// 输出结果如下:
[ 'abcd', 0, 'abcd 2023 abcd' ]
[ 'abcd', 10, 'abcd 2023 abcd' ]
2023 2023 2023
通常正则表达式会包含一些 分组 的逻辑,如我们要对时间的分隔符进行替换,我们通过正则可以这样实现:
const time = '2023-03-01';
const reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
console.log(time.replace(reg, '$1年$2月$3日')); // 2023年03月01日
正则通过 ()
元字符实现分组,这时 RegExc
原型上就为我们提供了每一个分组匹配结果,通过 $1-xx
来记录。
对于第二参数为函数,同样也可以适应分组匹配:
const time = '2023-03-01';
const reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
console.log(time.replace(reg, (target, $1, $2, $3) => {
console.log(target, $1, $2, $3); // 2023-03-01 2023 03 01
return `${$1}年${$2}月${$3}日`;
})); // 2023年03月01日
在实际业务中,replace
可以做的事情有很多,如解析模板语法 {{var}}
实现变量替换,下面我们来看看几个常见的应用场景。
四、工作上常见的正则使用场景
1. 驼峰命名转为短横线(-)命名
使用过 React JSX 的同学都知道:为元素设置 style 属性时需要采用 小驼峰 命名,如:fontSize: 16px
,所以我们定义样式时会采用这种方式。
我们回顾一下原生 HTML 为 DOM 添加样式的方式:<div style="font-size: 12px;"></div>
采用短横线 - 命名方式。
假设现在有一个需求:将 React JSX 节点及样式生成原生 HTML 模板文件提供给后台使用。
那我们知道:小驼峰 fontSize
肯定不能正常运行在 HTML 内的,需要转换为 font-size
,正则可以帮助我们快速实现:
'fontSize'.replace(/[A-Z]/g, val => `-${val.toLowerCase()}`);
2. 模板变量替换
有时候我们需要对一段字符串模板进行解析,将模板内的插槽替换为实际变量,replace
可以很方便的实现。假设我们有模板数据:
let str = "{{user_name}} - {{user_sex}}";
借助正则分组捕获,我们可以很轻松的实现变量捕获及替换:
str = str.replace(/{{(\w+)}}/g, (content, $1) => {
console.log(content, $1);
return $1 === 'user_name' ? '明里人' : '男'
});
console.log(str);
打印输出如下:
{{user_name}} user_name
{{user_sex}} user_sex
明里人 - 男
更新中...
最后
感谢阅读,如有不足之处,欢迎指出。
转载自:https://juejin.cn/post/7205522148785471544