JavaScript正则总结

424 阅读4分钟

正则表达式的主要作用,要么匹配模式,要么匹配位置, 要么匹配字符。

字符匹配攻略

模糊匹配: 横向 & 纵向

横向模糊匹配 (长度不固定)

横向模糊是指:长度不固定,可以是多种情况。
实现

使用量词,{m,n},连续出现最少m次,最多n次。

纵向模糊匹配 (某一位字符不确定,字符组实现)

纵向模糊是指:一个正则匹配的字符串,具体到某一位字符时,它可以不是某个特定的字符,可以是多种可能。
实现

使用字符组,eg [123]表示该字符可以是a、 b、c 任意一个字符

字符组 -- 只是其中字符之一

范围表示法

连字符 - 表示省略和简写 [a-z] :全部小写字符 [-az]、[az-]、[a-z]:表示a、-、z三者任意一个字符

排除字符组/反义字符组[^]

可以是任何东西,但特定几个除外 字符组第一位放^

常见简写形式

字符组具体含义
\d[0-9],digit数字
\D[^0-9],除数字以外的任何字符
\w[0-9a-zA-Z],数字、大小写字母和下划线,word
\W[^0-9a-zA-Z],非单词字符
\s[\t\v\n\r\f],空白符(空格、水平制表符、垂直制表符、换行符、回车符、换页符)
\S[^\t\v\n\r\f],非空白符
.[^\n\r\u2028\u2029],通配符,占位符

量词

简写形式

量词具体含义
{m,}至少出现m 次
{m}出现m次
?{ 0,1}
+{1,}
*{0 ,}

贪婪匹配 & 惰性匹配

贪婪匹配:尽可能多匹配 惰性匹配: 尽可能少匹配 量词后面加个? 实现惰性匹配

惰性量词贪婪量词
{m,n}?{m.n}
{m, }?{m,}
???
+?+
*?*

多选分支(惰性匹配,多个子模式任选一)

多选分支:支持多个子模式,任选其一。

具体形式:(p1 | p2 | p3)

Eg: /good|nice ,匹配字符串good 或nice

多选分支是惰性匹配的,只要前面匹配上了,后面就不再匹配。 /good|goodbye 匹配goodbye 时只会匹配到good 就结束

练习

  1. 16进制颜色值匹配: /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g
  2. 匹配时间,24小时制: /^([01][0-9]|[2][0-4]):[0-5][0-9]$/
  3. 匹配日期 yyyy-mm-dd格式 /^([0-9]{4}-(0[1-9]|1[1-2])-(0[1-9]|[12][0-9]|3[101]))$/
  4. window操作系统文件路径
    • 模式: 盘符:\文件夹\文件夹
  5. 匹配id,从 <div id=“container” class=“main”></div> 中匹配ID */id=".*?"/ VS /id="[^"]*"/

位置匹配攻略

位置是指:相邻字符之间的位置

含义举例
^匹配开头
$匹配结尾
\b单词边界,具体就是\w和\W之间的位置
\B非单词边界
(?=p)正向先行断言,p是一个子模式,p前面的位置,即接下来的字符与p匹配'hello'.replace(/(?=l)/h,'#') => he#l#lo
(?!p)反向先行断言'hello'.replace(/(?=l)/h,'#') => #h#ell#o
(?<=p)正向环视
(?<!p)反向环视

练习

1.数字的千位分隔符表示法

查看答案 从后往前数,且不是开头:

num.replace(/ (?!^)(?=(\d{3})+$)/g,',')


2. 货币格式化 1888 -->$ 1888.00
查看答案

num.toFixed(2).replace(/\B(?=(\d{3})+\b)/g,',').replace(/^/,'$$ ');


  1. 校验密码,6-12位(数字+小写字符+大写字母),但必须包含2中字符
查看答案 + 数字+小写 + 数字 + 大写 + 小写 + 大写 + 数字 + 小写 + 大写
`reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*))`

匹配原理

括号的作用- 分组 和 分支结构

1.分组

括号提供分组功能

Eg: /ab+/ 和 /(ab)+/

2.分组引用

正则引擎,在匹配过程中,给每个分组都开辟一个空间,用来存储每一个分组匹配到的数据

  1. 提取数据
    • match,返回一个数据,第一个返回整体匹配结果,第二个返回各个分组匹配的内容,第三个返回匹配的下表,最后返回输入的文本
  2. 替换
    • replace,第一个参数事正则表达式,第二个参数里用11 2 $3 指代相应分组;

3.反向引用 -- 正则本身引用分组

反向引用指 在正则本身里引用分组,但只能引用之前出现的分组 todo
Eg: /\d{4}(-|/|.)\d{2}\1\d{2}/ \1 表示引用之前的第1个分组(-|/|.)

思考:括号嵌套怎么办?

Eg:/^((\d)(\d(\d)))\1\2\3\4$/

\10 表示第10个分组
引用不存在的分组不会报错,会匹配反向引用的字符本身 比如 \2 就会匹配 "\2"

var regex = /\1\2\3\4\5\6\7\8\9/;
console.log( regex.test("\1\2\3\4\5\6\7\8\9") );
console.log( "\1\2\3\4\5\6\7\8\9".split("") );
分组后有量词,最终捕获的是 最后一次匹配的数据
var regex = /(\d)+/;
var string = "12345";
console.log( string.match(regex) );
// => ["12345", "5", index: 0, input: "12345"]

反向引用也是

var regex = /(\d)+ \1/;
console.log( regex.test("12345 1") );
// => false
console.log( regex.test("12345 5") );
// => true

4.非捕获括号 -- (?:p) (?:p1|p2|p3)

如果 ==既不在API里引用,也不在正则里反向引用==,只想要括号的最原始功能,并不会引用它,可以使用非捕获括号(?:p) 和 (?:p1|p2|p3)

练习

  • 字符串trim方法模拟

    查看答案trim 去掉字符串开头和结尾的空符

    str.replace('/^\s+|\s+$/g','')

  • 每个单词首字母大写

    查看答案trim 去掉字符串开头和结尾的空符
       `str.toLowerCase().replace('/(?:^|\s)\w/g', (c) => {
              return c.toUpperCase();
          })`
          
    
  • 驼峰化

    查看答案
    str.replace(/[-_\s]+(.)?/g, (c) => {
            return c ? c.toUpperCase() :'';
        })
    
  • 驼峰 => 中划线

    查看答案
    str.replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();
    
  • 匹配成对标签 匹配 <title></title> 不匹配<title></p>

反向引用:/<([^>]+)>[\d\D]*<\/\1>/

回溯法原理

reg = /ab{1,3}c/

str = 'abbbc'时,是**==没有回溯的匹配==**
str = 'abbc' 时,中间就有回溯

常见的回溯形式

回溯法:也称试探法基本思想是 从问题的某一个状态(初始状态)出发,搜索从这种状态所能达到的所有状态,当一条路走到尽头不能再前进时,再后退一步或若干部,从另一种可能状态出发,继续搜索,直到所有的路径都试探过。这种不断前进回溯寻找解的方法,叫回溯法。
本质上就是深度优先搜索算法,其中推到之前的某一步的这个过程,我们称之为“回溯”。简单来说,就是路走不通时就会发生回溯。尝试匹配失败时,下一步通常就是回溯。

正则引擎

DFA:确定型有限自动机 NFA:确定型有限自动机.JavaScript 是NFA

正则表达式的拆分(读)

结构和操作符

结构:优先级 从上到下,由高到低,分支最低

结构说明
字面量匹配一个具体字符
字符组匹配一个字符,可以是多种可能之一
量词表示一个字符连续出现的次数
匹配一个位置,而不是字符,比如 开头 和 结尾
分组用()表示一个整体 ,Eg:(ab)+ VS (?:ab)+
分支多个子表达式多选一

操作:优先级 从上到下,由高到低

操作符描述操作符
转义符\
括号啊和方括号(...)、(?:...)、(?=...)、(?!...)、[...]
量词限定符{m}、{m,n}、{m,}、?、*、+
位置和序列^、$、\元字符、一般字符
管道符(竖杠)|

注意要点

  • 量词连缀需要加括号

练习

1、匹配身份证

/^(\d{15}|\d{17}[\dxX])$/

2、IPV4地址

/^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/

正则表达式的构建(写)

平衡法则

  • 匹配预期的字符串
  • 匹配非预期的字符串
  • 可读性和可维护性
  • 效率

构建正则前提

  • 是否能使用正则?
  • 是否有必要使用正则?能用字符串API解决的简繁问题就用api
  • 是否有必要构建一个复杂的正则?

准确性

准确性,是能匹配预期的目标

  • 了解各部分的模式规则
  • 明确形式关系

效率

正则表达式运行过程:

  1. 编译
  2. 设定起始位置
  3. 尝试匹配
  4. 匹配失败,从下一位开始继续第3步
  5. 最终结果:匹配成功或失败

优化效率

  • 使用具体型字符组来代替通配符,来消除回溯
  • 使用非捕获型分组
  • 独立出确定字符
  • 提取分支公共部分
  • 减少分支的数量,缩小它们的范围

相关API

字符串API

  • search
  • split
  • match
  • replace

正则API

  • test
  • exec
  • search
  • split
  • match
  • replace

参考链接

参考书

  • 《JavaScript正则迷你书》

正则可视化编辑调试器