正则很强大也很重要。是时候写个笔记整理整理了。


写在前面

这篇文章主要是根据老姚的《JavaScript正则表达式迷你书(第一版)》所做的笔记,然后根据阮老师的es6教程进行了一些扩展,算是系统的学了一遍正则。

字符匹配

  1. 横向匹配和纵向匹配

    1. 横向匹配   —>   同一字符,重复出现   —>   量词{m, n}
    2. 纵向匹配   —>   不同字符,出现一次   —>   字符组[abc]
  2. 量词

    1. 简写
      • {m, }   —>   至少m次
      • {m}   —>   m次
      • ?   —>   出现或不出现(0次或1次)
      • +   —>   至少1次
      • *   —>   任意次
    2. 贪婪匹配与惰性匹配
      直接写量词   —>   贪婪匹配   —>   匹配出所有满足条件的
      量词后面加?   —>   惰性匹配   —>   只匹配出第一次满足条件的
  3. 字符组

    1. 范围表示   —>   连字符-   —>   [a-z]
    2. 排除(不包含)表示   —>   脱字符^   —>   [^a-z]
    3. 常见的简写
      • \d\D)  —>   一位(非)数字   —>   [0-9][^0-9]
      • \w\W)  —>   一位(非)数字、字母、下划线   —>   [0-9a-zA-z_][^0-9a-zA-z_]
      • \s\S)  —>   一位(非)空白符   —>   [ \t\v\n\r\f][^ \t\v\n\r\f]
      • .   —>   通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。
      • 任意字符用[\d\D]``[\w\W]``[\s\S]``[^]四者之一
      • es6+ 提案:增加了s修饰符,以支持用.匹配所有字符。
  4. 多选分支
    用管道符|分割,惰性的(即匹配到一个后,之后的就不匹配)

位置匹配

  1. 锚点
    1. 匹配开头和结尾
      • ^$
      • 多行匹配模式(即有修饰符 m)时,匹配行开头和行结尾
    2. 单词边界和非单词边界
      • \b\B
      • 单词边界就是\w\W之间的位置,以及\w^之间的位置与\w$之间的位置
    3. 具体位置
      • p 是个子模式。
      • (?=p) 表示p前的位置,也就是这个位置的后面的字符必须匹配p
      • (?!p) 表示非/不是/除了p前的位置,也就是这个位置的后面的字符必须不匹配p
      • es6+ 提案(?<=p)(?<!p)表示p之后
  2. 特性
    • 位置可理解为空字符“”
    • 两个字符间的一个位置可以用重复多个锚点表示   —>   这个特点常用来限定一个位置需要满足一定的条件(比如:(?=.*[0-9])^表示开头之后的字符必须包含一个数字,(?=.*[0-9])^都表示开头的位置)

关于括号

  1. 分组
    将一个模式作为整体,以便后面加量词。或者将分支结构作为整体防止歧义
  2. 提取数据和替换
    分组之后就可以方便的操作每一个分组。全局属性$1$9的使用
  3. 反向引用
    在表达式中引用之前出现的分组。\1``\2…的使用
    • 括号嵌套   —>   以左括号为准
    • \10   —>   表示第10个分组 (表示\10,使用(?:\1)0或者\1(?:0)
    • 引用了不存在的分组   —>   当作普通字符进行匹配字符本身
    • 分组后有量词   —>   分组最终捕获到的数据是最后一次的匹配
  4. 非捕获括号
    单纯使用括号的分组功能,不会涉及引用功能。   —>   在之前的左括号后加上?:,即(?:p)
  5. es6+ 提案
    • 支持为每个分组取别名。之前的用数字表示法依然有效,不冲突。
    • 语法:(?<groupName>p)
    • 在正则里反向引用的语法:\k<groupName>
    • 在replace替换时引用分组的语法:$<groupName>
    • 此外,可以在exec方法返回结果的groups属性上引用该组名

回溯原理

常见的回溯形式

  • 贪婪量词
  • 惰性量词
  • 分支结构

拆分

操作符优先级(由高到低)
转义符、括号、量词、位置和序列、管道符

构建

  1. 平衡法则
    • 匹配预期的字符串
    • 不匹配非预期的字符串
    • 可读性和可维护性
    • 效率
  2. 优化方法
    • 使用具体型字符组来代替通配符,消除回溯
    • 使用非捕获型分组
    • 独立出确定字符
    • 提取分支公共部分
    • 减少分支数量,缩小范围

编程

  1. 验证(常用test
    • !!~string.search(regex)
    • regex.test(string)
    • !!string.match(regex)
    • !!regex.exec(string)
  2. 切分 string.split(regex)
  3. 提取(常用match
    • string.match(regex)
    • regex.exec(string)
    • regex.test(string); console.log(RegExp.$1)
    • string.test(regex); console.log(RegExp.$1)
    • 使用replace,将函数作为第二个参数
  4. 替换 replace
  5. 注意点
    • searchmatch会把字符串转换为正则
    • match返回结果的格式,与正则对象是否有修饰符g有关。
      • 没有g,返回这样的数组:第一个元素是整体匹配的内容,接下来是分组捕获的内容,然后是整体匹配的第一个下标,最后是输入的目标字符串。
      • g,返回所有匹配内容的数组
    • g修饰符时,execmatch强大
      exec会记录上次匹配的位置,接着上次匹配后继续匹配。