Regular Expression

RegExp 这个坎始终还是得踏实的迈过去 :)

正则表达式是描述字符串结构模式的形式化表达方法。在不同的宿主语言或宿主工具中使用正则表达式存在几点不同:流派(所实现的元字符数量和意义),交互(支持的操作类型、方法和对象),实现(对正则表达式的实现方式和原理)。下面主要针对 JavaScript 中的正则实现进行整理总结。

1. 语法

正则表达式中可以使用各种特殊的字符和语法,从而构建复杂的匹配表达式。

1.1 字符(仅一种可能性的单个字符)

  • 转义字符 \char: 如果 char 是元字符,或无特殊含义的字符,匹配 char 对应的普通字符(如 \*);如果 char 是有特殊含义的字符,则形成一种由宿主定义并实现的转义序列,并按其功能进行匹配(如 \b
  • 特殊字符:[\b](退格符)\f(换页符)\n(换行符)\r(回车符)\t(水平制表符)\v(垂直制表符)\cX(控制符,其中 X 为 A-Z 之间的字母)
  • 编码字符
    • \0:NULL 字符(U+0000)
    • \0xxx:与代码对应的编码字符,其中 x 为八进制数字
    • \uhhhh\u{hhhh}\xhh:与代码对应的编码字符,其中 h 为十六进制数字

1.2 字符组(有多种可能性的单个字符)

  • 普通字符组
    • 点号 .:匹配单个任意字符(换行符除外)
    • 字符组 [···]:匹配单个列出的字符(包括点号星号等特殊字符,且无需转义),可使用 - 表示范围
    • 排除型字符组 [^···]:匹配单个未列出的字符
  • 通用字符组
    • 空白符 \s:包括空格、换行、回车、制表等空白字符(非空白符为 \S
    • 数字符 \d:即 [0-9](非数字符为 \D
    • 单字符 \w:即 [0-9a-zA-Z_](非单字符为 \W

1.3 零宽断言(只匹配位置,不匹配字符)

  • 起始
    • 脱字符 ^:匹配整个字符串的开始(处于多行模式时,还匹配换行符后的位置,即下一行的开始)
    • 美元符 $:匹配整个字符串的结束(处于多行模式时,还匹配换行符前的位置,即上一行的结束)
  • 分界
    • 单词边界符 \b:匹配单词的边界,即前后两个字符的类型不同
    • 非单词边界符 \B:匹配非单词的边界,即前后两个字符的类型相同,都是单词字符或都是非单词字符
  • 环视
    • 肯定顺序环视 (?=···):匹配一个位置,该位置的右侧能够匹配子表达式
    • 否定顺序环视 (?!···):匹配一个位置,该位置的右侧不能匹配子表达式

注:环视通常与子表达式配合使用,如 x(?=y) 表示仅当 x 后面紧跟 y 时才匹配 x,x(?!y) 表示仅当 x 后面不紧跟 y 时才匹配 x

1.4 其他语法

  • 分支 ···|···:匹配多选结构中的任意一个分支表达式
  • 捕获
    • 捕获型括号 (···):限定多选结构或量词作用的范围(分组),并为反向引用捕获文本(捕获)
    • 非捕获型括号 (?:···):只分组,不捕获
    • 反向引用 \num:引用前面第 num 个括号内的子表达式匹配到的文本。如果不是匹配环节,而是替换环节,则通过 $num 使用文本
  • 量词
    • 问号 ?:匹配 0 次或 1 次
    • 星号 *:匹配 0 次以上
    • 加号 +:匹配 1 次以上
    • 定值 {n}:匹配 n 次
    • 开区间 {n,}:匹配 n 次以上
    • 闭区间 {n, m}:匹配至少为 n 次,至多为 m 次

注 1:反向引用的括号的 index 是自左向右查看左括号的位置来确定的(即使有嵌套),但要排除普通左括号 \( 注 2:量词单独使用时,默认为贪婪模式(尽可能多的匹配),量词后紧跟 ? 则为非贪婪模式(尽可能少的匹配)

2. 使用

2.1 构建表达式

可以使用字面量形式或者构造函数来构建一个正则表达式:

// 字面量
var reg = /pattern/flags
// 构造函数
var reg = new RegExp('pattern', 'flags')

2.2 使用表达式

JavaScript 中的一些方法可以使用正则表达式:

  • RegExp.prototype.exec()
  • RegExp.prototype.test()
  • String.prototype.match()
  • String.prototype.search()
  • String.prototype.replace()
  • String.prototype.split()

2.3 选择匹配模式

用来规定正则表达式如何解释和应用,既可完全用于整个表达式(修饰符 modifier 或选项 options),也可单独用于某个子表达式(使用特殊的正则结构语法)。JavaScript 中仅能通过修饰符的方式用于整个表达式:

  • g 全局模式:找到所有的匹配,而非匹配成功一次后停止。下次匹配的起始位置是上次匹配的结束位置
  • i 不区分大小写模式:忽略字母的大小写
  • m 多行模式,即多行文本模式,实现脱字符和美元符能够匹配字符串内部的换行符的前后位置

3. 实例与细节

3.1 实例

  • 匹配标识符:[a-zA-Z_]\w*
  • 匹配段落间隔:多行模式下匹配行间隔中的空白符,即 ^\s*$
  • 匹配 email:\b(username\@hostname)\b,username 为 \w[-.\w]*,hostname 为 [-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)*\.(com|gov|edu|me)
  • 匹配 URL:\bhttp://hostname\b(/[-a-zA-Z0-9_:\@&?=+,.!/~*'%\$]*)?,hostname 同上
  • 匹配 IP:([01]?\d?\d|2[0-4]\d|25[0-5])(\.[01]?\d?\d|2[0-4]\d|25[0-5]){3}
  • 实现单词分界符 \b(?<!\w)(?=\w)|(?<=\w)(?!\w)
  • 处理 HTML 特殊字符:先把 & 替换为 &amp;,再把 <> 分别替换为 &lt;&gt;

3.2 细节

在宿主语言中,如果通过字符串文本来提供正则表达式(当然可以直接用正则文本)时,则必须要清楚“字符串文本”在处理为“字符串值”后,交给正则引擎的“正则表达式”究竟有没有变化。

将来自用户输入的正则字符串转换为正则字面量的字符串:

function escapeRegExp(string){
  return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$&"); 
}

正则表达式的其他一些细节:

  • 在字符组中,元字符通常表示普通字符,反斜杠也只是普通的反斜杠字符而非转义功能的字符
  • 连续的字符,在字符组内表示“或”,在字符组外表示“接下来是”
  • 连字符,在字符组内(非首个字符)表示“范围”,在字符组内(首个字符)以及在字符组外表示“普通连字符”
  • 脱字符,在字符组内(首个符号)表示“排除”,在字符组内(非首个字符)表示“普通脱字符”,在字符组外表示“行锚点”
  • 点字符,在字符组内表示“普通点字符”,在字符组外表示“单个任意字符”
  • 多选结构和字符组仅在极少数情况下是功能类似的
  • 排除型字符组依然是肯定断言,即需要匹配一个字符
  • 顺序环视和逆序环视结合使用,会精准的确定一个位置
  • 通常 \n(换行符)表示一行的终结,但 Windows 下则是 \r\n(回车 + 换行)
  • 元字符存在多种层级,如语言或工具宿主,正则表达式,字符组,字符串,shell

参考

  1. 精通正则表达式
  2. 正则表达式 - JavaScript | MDN

results matching ""

    No results matching ""