前端正则表达式学习和实践

前言

最近的项目里有多处格式和查询使用到了正则表达。简单的正则语法会写了,但是一直没有花功夫研究遇到的复杂正则。刚好趁着这一波研究的兴趣,总结一下自己学习到的东西,不再没有灵魂的 google 后复制粘贴了。以后再看别人写的优雅代码,再也不用眼馋了。

推荐工具

  1. regex用来测试
  2. regexper用来解析

regexper的解析真的非常有用处,它可以图形化的展示出一个正则的所有的逻辑分支。对于自己难以读懂的长正则,或者自己写的复杂正则,都可以先解析成图形化的逻辑来分析,大大降低了难度。如下图随便写的一个正则解析:

正则表达式基础知识

字符类

字符 匹配 字符 匹配
[] 方括号内的任何字符 [^] 不在方括号内的任何字符
\w [a-zA-Z0-9] \W [^a-zA-Z0-9]
\s 任何Unicode空白字符 \S 任何Unicode空白字符
\d [0-9] \D [^0-9]
. 除了换行符和其它Unicode终止符之外的任意字符 \b 单词的边界。\w和\W之间的边界。

重复

字符 含义
{n,m} n <= matches < m
{n,} matches >= n
{n} matches === n
? {0,1}
+ {1,}
* {0,}

注意:

*? 可以匹配0个字符,因此它们允许什么都不匹配。

/a*/.test('bbbb')   //=> true
复制代码

非贪婪重复

正常匹配重复字符是尽可能多的匹配,我们称之为“贪婪的”匹配。

非贪婪的匹配级尽可能的少匹配。只需在待匹配字符后加一个问号

// 贪婪匹配
'bbbb'.match(/b+/g)   // => ["bbbb"]
// 非贪婪匹配
'bbbb'.match(/b+?/g)   // => ["b", "b", "b", "b"]
复制代码

正则表达式匹配总是会寻找字符串中第一个可能匹配的位置。因此不会考虑它子串中更短的匹配。

'aaab'.match(/a+b/g)  // => ["aaab"]
// 你可能想得到 ’ab‘, 但是并不会。
'aaab'.match(/a+?b/g)  // => ["aaab"]
复制代码

选择

字符 含义
| 或关系

引用

字符 含义
() 1. 把单独的项合成子表达式,以便像处理一个独立单元那样使用。
2. 在完整模式中定义子模式。当一个正则表达式成功和目标字符串匹配时,可以从目标串中抽出子模式匹配的部分。
3. 允许在同一个正则表达式的后面引用前面的子表达式。\1 引用的是第一个带圆括号的子表达式。 \3引用的是第三个。因为可以嵌套,所以是按参与计数的左括号的位置来决定。
\n 和第 n 个分组匹配的字符串匹配

引用的一个好处就是并不是子表达式相同,而是与引用子表达式模式匹配的文本相等。即一个字符串中各个部分包含的是完全相同的字符串。

例如匹配单引号或双引号:

// 它并不要求左右 单双引号 是匹配的
const partten = /['"][^'"]*['"]/g
// 改为
const partten = /(['"])[^'"]*\1/g
复制代码

分组

字符 含义
() 可以记住和这个组合匹配的字符串以供以后的引用使用
(?:) 单纯分组,不记住与该组合匹配的字符串

锚点

类似 \b不匹配任何字符,指定匹配发生的合法位置。有时我们成为正则表达式的锚。

字符 含义
\b 单词边界(a word boundary),\w和\W之间的边界,或位于一个单词与字符串开始和结束之间的边界。
\B 非单词边界(Non-word boundary)
^ 字符串的开始
$ 字符串的结束

前后关联约束

字符 含义
(?=) 前置约束-存在
(?!) 前置约束-排除
(?<=) 后置约束-存在
(?<!) 后置约束-排除

修饰符

字符 含义
i 执行不区分大小写
g 找到所有匹配,否则在找到第一个后就停止
m 多行匹配,^匹配一行的开头,$匹配一行的结束

JavaScript基础知识

用于模式匹配的 string 方法

String.prototype.search()

返回第一个满足条件的匹配结果在整个字符串中的位置。如果没有任何匹配,则返回-1。
注意: 不支持全局搜索。只返回第一个匹配的位置信息。

"This is a test text".search(/th/i)  // => 0
复制代码

String.prototype.replace()

字符串对象的replace方法可以替换匹配的值。它接受两个参数,第一个是正则表达式,表示搜索模式,第二个是替换的内容。

var str = '  #id div.class  ';

str.replace(/^\s+|\s+$/g, '')
// "#id div.class"
复制代码

replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容。

符号 含义
$& 匹配的子字符串
$` 匹配结果前面的文字
$’ 匹配结果后面的文字
$n 匹配成功的第n组内容,n从1开始。
$$ 指代美元 $

replace方法的第二个参数还可以是一个函数,将每一个匹配内容替换为函数返回值。

var a = 'The quick brown fox jumped over the lazy dog.';

a.replace(pattern, function replacer(match) {
  return match.toUpperCase();
});
复制代码

String.prototype.match()

返回匹配的数组或null。 g 修饰符有效。

String.prototype.split()

str.split(separator, [limit])
复制代码

RegExp对象

RegExp.prototype.test()

返回布尔类型

如果正则表达式带有g修饰符,则每一次test方法都从上一次结束的位置开始向后匹配。

带有g修饰符时,可以通过正则对象的lastIndex属性指定开始搜索的位置。

var r = /x/g;
var s = '_x_x';

r.lastIndex = 4;
r.test(s) // false
复制代码

如果正则模式是一个空字符串,则匹配所有字符串。

RegExp.prototype.exec()

正则实例对象的exec方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null

var s = '_x_x';
var r1 = /x/;
var r2 = /y/;

r1.exec(s) // ["x"]
r2.exec(s) // null
复制代码

如果正则表示式包含圆括号(即含有“组匹配”),则返回的数组会包括多个成员。第一个成员是整个匹配成功的结果,后面的成员就是圆括号对应的匹配成功的组。也就是说,第二个成员对应第一个括号,第三个成员对应第二个括号,以此类推。整个数组的length属性等于组匹配的数量再加1。

var s = '_x_x';
var r = /_(x)/;

r.exec(s) // ["_x", "x"]
复制代码

exec方法的返回数组还包含以下两个属性:

  1. input:整个原字符串。
  2. index:整个模式匹配成功的开始位置(从0开始计数)。

如果正则表达式加上g修饰符,则可以使用多次exec方法,下一次搜索的位置从上一次匹配成功结束的位置开始。

练习题

基础知识已经写的差不多了。记住使用说明的最好方式就是做做题啦。

  1. 与搜索字符串开始处的 3 个数字匹配。
  2. 与除 a、b 和 c 以外的任何字符匹配。
  3. '1234567'.match(/\d{1,3}/g)的结果。(贪婪匹配)
  4. 不以“th”开头的单词匹配。
  5. 去除字符串首尾的空格。
  6. 三分位格式化一个数字。
  7. 对密码应用以下限制:其长度必须介于 4 到 8 个字符之间,并且必须至少包含一个数字。
  8. 获取url中的属性对应的值

还有一些正则练习的网站:

regexone.com/

callumacrae.github.io/regex-tuesd…

如果大家知道有意思的正则题目,欢迎分享哇~

参考资料

  1. RegExp对象
  2. 正则表达式实践篇
  3. learn-regex
  4. JavaScript权威指南(第六版)

猜你喜欢

转载自juejin.im/post/5c6d18e56fb9a04a0a5fc4bf
今日推荐