正则笔记

日常开发中,经常需要进行一些文本处理,这通常是十分繁琐而无趣的。学会并利用正则表达式可以快速解决这类文本处理问题,无论是在 Java,Python 等代码中抑或是 shell 环境下。
正则中存在很多细小的知识点,十分容易遗忘,着手记录,知识整理还是有所必要。

本文以拓展正则进行描述,部分特殊字符和定址方式在标准正则下无效。如需在 shell 环境下使用,需要额外使用‘\’符号,或开启拓展正则的支持,如 grep -e,sed -r 等。

规则

正则表达式的规则可以简单理解为:在给定的范围内,给定的字符重复给定的次数

指定需要匹配的字符

绝大部分的字符,包括数字字母汉字等,都可以直接输入来描述。

转义后特殊字符

以下几个字符由转义符‘\’和某个字符组成,可以表述成新字符,他们有着特殊的含义。

  • \n 这是一个换行符。
  • \t 制表符,Tab 键的缩进。
  • \v 垂直制表符。
  • \f 分页符,产生一个新页。
  • \r 这是回车符
  • \s 这个表达可以表述所有的空白字符,即以上五种字符或者空格。
  • \cx 当 x 取值英文字符时,这个整体就有了特殊意义,会映射成一个控制字符,如 \cM 等价于 \r 即回车符号。
  • \ux 可以匹配 Unicode 字符,如 \u00CD。
  • \nx 在 x 值合法的情况下,可以匹配八进制专一值(在没有顺利取得缓存引用时)
  • \b 表示字符边界,可以理解为打断单词或着汉字连续的空格/符号之类(其实它并不能匹配到某个字符,仅仅是匹配到了边界)
  • \d 表示数字,同 [0-9]
  • \w 表示任意字类字符,即数字字母和下划线,通常还支持汉字。
  • \< 匹配单词首,比如 \<wo 可以匹配 wood,word 等。
  • \> 同理匹配单词尾部。

原生特殊字符

以下的字符并不需要转移符‘\’,天然的拥有特殊含义(以拓展正则为准)。

  • $ 尾部,表示字符串结尾位置。
  • ^ 头部,字符串的开头位置,但在 [ ] 内使用时表示取反
  • [ ] 左右中括号,用来表达匹配单个字符时候的可选范围
  • { } 左右花括号,用来表述前一表达式的可重复区间
  • ( ) 左右小括号,类似数学中的概念,可以描述一个表达式并提高计算优先级,同时会缓存匹配结果。
  • · 点号,可以用了匹配任意的一个字符,除了 \n 吧。
  • * 星号,可以匹配前面表达式任意次数。
  • + 加号,匹配前面表达式一至任意次数.
  • ? 问号,匹配前面表达式 0 ~ 1 次。
  • \ 转移符本身,用来转移其他字符,需要匹配它本身的时候自然需要 \\ 的形式。
  • | 或运算符号,任意匹配前后表达式之一。
  • [:digit:] 所有数字
  • [:lower:] 所有小写字母
  • [:upper:] 所有大写字母
  • [:alpha:] 所有字母
  • [:alnum:] 所有字母及数字
  • [:space:] 所有空白符
  • [:punct:] 所有标点符号

指定匹配字符的候选范围

匹配一个单字符很简单,但是通常我们需要匹配几个字符中的任意一个,这个时候就需要一个候选范围的描述。
除了使用 \w 表示字类字符,\s 来表示空白符。也可以使用 [ ] 方括号来描述范围,来看几个例子:

  • [abc] 它可以是匹配 a,也可以是 b,也可以是 c。
  • 5[48]k 它可以是 54k,也可以是 58k。
  • [0-9]\w 它可以使是 0a,3b,33 等等。

这样一看是不是很容易?来看一些进阶技巧:

  1. 取反。表述范围的符号可以通过大写来表示取反,\S 表示非空白字符,\B 表示非字符边界,\W 表示非字类字符。对于使用 [ ] 的场合,使用 [^ ] 来完成取反。
  2. [ ] 方括号中间不再能使用上述特殊的字符,比如 [x*],* 不再匹配任意次 x,这个表达式只能匹配 * 或 x;同理比如:[\s] 表示 \ 或者 s。
  3. [ ] 中可以使用一些特定的范围,比如 0-9,a-z,A-Z。比如式子 [0-9A-Z] 也是合理的,会匹配数字或者大写字母,如需要匹配‘-’,尽量写在最后。

指定表达式的重复次数

在需要重复一个特定的正则表达式的时候,我们可以使用限定符描述重复次数来简化它。

  • {n} 匹配 n 次,如 C{3} 即 CCC。
  • {n,} 匹配大于等于 n 次。如 C{1,} 等同于 C+
  • {n,m} 匹配大于等于 n 次,小于等于 m 次,闭区间。
  • * 等同于 {0,}
  • + 等同于 {1,}
  • ? 等同于 {0,1}

利用正则匹配的缓存

  • \num 使用 ( ) 之后会进行匹配值的缓存,可以紧跟着 \num 指定匹配的子项,比如 (.)\1 可以匹配任意两个相同的字符。或者在 sed,vim 等工具使用替换操作时,可以在新字符串上使用 \num 以期达到精确的匹配但是局部替换的效果。
  • (?:pattern) 使用该符号会取消匹配值的缓存
  • (?=pattern) 正向肯定预查,使用该符号会取消匹配值的缓存,同时预查也是不消耗字符的。
  • (?!pattern) 反向预查,相反于前者
码路加油