正则表达式 (Regular Expression) 在代码中常简写为regex、regexp 或 RE,计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。

  • 列目录时,dir *.txtls *.txt中的*.txt就不是一个正则表达式,因为这里与正则式的含义是不同的。
  • 构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与运算符可以将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。

正则表达式在线测试 | 菜鸟工具 (runoob.com)
正则可视化工具 | REGEXPER

正则表达式字符

  • 元字符:元字符是正则表达式的基本元素。匹配元字符自身时需要加斜杠\(在R中斜杠为\\,匹配+应写为\\+
  • 普通字符:普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。可直接匹配自身。

非打印字符

非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列

字符 描述
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。
x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\p 匹配 CR/LF(等同于 \r\n),用来匹配 DOS 行终止符

打印字符

区间 描述
x | y 或,优先级最低
[xyz] 字符集合,匹配任意一个
[a-z] 小写字母
[0-9] 数字
[^xyz] 匹配不在集合里的任意字符
[^a-z] 除了小写字母
[A-Z] 大写字母
字符 描述 等价
. (dot) 匹配除换行符、制表符之外的任何单个字符 [^\n\r]
\d 匹配一个数字 [0-9]
\D 匹配一个非数字 [^0-9]
\w 匹配字母、数字、下划线 [A-Za-z0-9_]
\W 匹配非字母、数字、下划线 [^A-Za-z0-9_]

限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。

限定符 描述 等价
* 匹配前面的子表达式零次或多次 {0,}
+ 匹配前面的子表达式一次或多次 {1,}
? 匹配前面的子表达式零次或一次 {0,1}
{n} 前面的子表达式确定匹配n次
{n,} 前面的子表达式至少匹配n次
{n,m} 前面的子表达式匹配>=n且<=m次

注意:限定符都是贪婪匹配,在之后放置 ?,可转换为最小匹配(如\*?)

定位符

定位符 描述 示例
$ 匹配结尾位置 (.csv)$
^ 匹配开始位置 ^(Windows)
\b 匹配单词边界位置 er\b 可以匹配never 中的 er,但不能匹配 verb 中的 er
\B 匹配非单词边界位置 er\B 能匹配 verb 中的 er,但不能匹配 never 中的 er

分组

用圆括号 () 包含的内容将会被看成一个整体,表示分组。 () 会把每个捕获分组里的内容保存起来, 可以用分组编号 \n 来获取分组内容,其中 n 是一个数字,表示第 n 个捕获组的内容。当 n=0 时匹配全部分组。

选择 描述 举例
(pattern) 匹配pattern并获取结果
(?:pattern) 非捕获组,匹配 pattern 但不获取匹配结果 Win(?:7\|10)相当于Win7\|Win10简略版
(?=pattern) 正向肯定预查,匹配pattern表达式前面的内容,但不返回本身 Win(?=XP)能匹配WinXP中的Win,但不能匹配Win10中的Win
(?!pattern) 正向否定预查,匹配非pattern表达式前面的内容,但不返回本身 Win(?!XP)能匹配"Win10"中的Win,但不能匹配WinXP中的Win
(?<=pattern) 反向肯定预查,匹配pattern表达式后面的内容,但不返回本身 (?<=7)Win能匹配7Win中的Win,但不能匹配10Win中的Win
(?<!pattern) 反向否定预查,匹配非pattern表达式后面的内容,但不返回本身 (?<!7)Win能匹配10Win中的Win,但不能匹配7Win中的Win
\n 对捕获分组的引用。 (.)\1 匹配两个连续的相同字符。

分组编号示例:比如电话号码 021-96110
捕获组正则表达式示例:(0\d{2})-(\d{5})

编号 分组 内容
0 (0\d{2})-(\d{5}) 021-96110
1 (0\d{2}) 021
2 (\d{5}) 96110

用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在圆括中来消除这种副作用。其中?:是非捕获元之一,还有两个非捕获元是 ?=?!

非捕获组正则表达式示例:(?:0\d{2})-(\d{5})

编号 分组 内容
0 (0\d{2})-(\d{5}) 021-96110
1 (\d{5}) 96110

局部替换:JS/nodeJS/Java 等语言可以使用 $1,$2,$3,...,$n 捕获第 n 个分组内容。下面的脚本使用replace()方法实现局部替换

> var re = /(\w+)\s(\w+)/;
> var str = "John Smith";
> var newstr = str.replace(re, "$2, $1");
> console.log(newstr);
Smith, John

标志

标志也叫模式修正符,因为它可以用来修改表达式的搜索结果。 这些标志可以任意的组合使用,它也是整个正则表达式的一部分。

标志 描述
i 默认是区分大小写的,i可以忽略大小写
g 正则遇到第一个匹配的字符就会结束,加上全局修复符,可以让其匹配到结束
m 多行修饰符:锚点元字符 ^ $ 工作范围在每行的起始。

附录

正则表达式实例

  • 汉字:^[\u4e00-\u9fa5]{0,}$
  • 禁止输入含有~的字符:[^~\x22]+
  • Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*.\w+([-.]\w+)*$
  • 身份证号(15位):^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$
    身份证号(18位):^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$
  • 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
    一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
  • 提取页面超链接:((?!.\brel=)[^>]*)(href="https?:\/\/)((?!(?:(?:www\.)?'.implode('|(?:www\.)?', $follow_list).'))[^"]+)"((?!.*\brel=)[^>]*)(?:[^>]*)>
  • 提取网页图片:\< *[img][^\\>]*[src] *= *[\"\']{0,1}([^\"\'\ >]*)
    提取网页颜色代码:^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$

R语言通用字符集

字符集合 描述
[[:punct:]] 标点符号,包括 !"#$%&'()*+,-./:;<=>?@[]^_{|}~ 和反引号
[[:alpha:]] 字母,等价于 [A-z]
[[:lower:]] 小写字母,等价于 [a-z]
[[:upper:]] 大写字母,等价于 [A-Z]
[[:digit:]] 数字,等价于 [0-9]
[[:xdigit:]] 十六进制,等价于[0-9A-Fa-f]
[[:alnum:]] 字母和数字,等级于[A-z0-9]
[[:cntrl:]] 控制符
[[:graph:]] 字母,数字和标点符号,等价于 [[:alnum:][:punct:]]
[[:print:]] 可打印字符,等价于 [[:alnum:][:punct:]\\s]
[[:space:]] 匹配任何空白字符等价于 [ \f\n\r\t\v]。
[[:blank:]] 空格和 Tab ,等价于 [ \t]