正则表达式

正则引擎

正则引擎主要可以分为两大类:一种是DFA,一种是NFA。这两种引擎都有了很久的历史(至今二十多年),当中也由这两种引擎产生了很多变体!于是POSIX的出台规避了不必要变体的继续产生。这样一来,主流的正则引擎又分为3类:一、DFA,二、传统型NFA,三、POSIX NFA。
DFA 引擎在线性时状态下执行,因为它们不要求回溯(并因此它们永远不测试相同的字符两次)。DFA 引擎还可以确保匹配最长的可能的字符串。但是,因为 DFA 引擎只包含有限的状态,所以它不能匹配具有反向引用的模式;并且因为它不构造显示扩展,所以它不可以捕获子表达式。
传统的 NFA 引擎运行所谓的“贪婪的”匹配回溯算法,以指定顺序测试正则表达式的所有可能的扩展并接受第一个匹配项。因为传统的 NFA 构造正则表达式的特定扩展以获得成功的匹配,所以它可以捕获子表达式匹配和匹配的反向引用。但是,因为传统的 NFA 回溯,所以它可以访问完全相同的状态多次(如果通过不同的路径到达该状态)。因此,在最坏情况下,它的执行速度可能非常慢。因为传统的 NFA 接受它找到的第一个匹配,所以它还可能会导致其他(可能更长)匹配未被发现。
POSIX NFA 引擎与传统的 NFA 引擎类似,不同的一点在于:在它们可以确保已找到了可能的最长的匹配之前,它们将继续回溯。因此,POSIX NFA 引擎的速度慢于传统的 NFA 引擎;并且在使用 POSIX NFA 时,您恐怕不会愿意在更改回溯搜索的顺序的情况下来支持较短的匹配搜索,而非较长的匹配搜索。
使用DFA引擎的程序主要有:awk,egrep,flex,lex,MySQL,Procmail等;
使用传统型NFA引擎的程序主要有:GNU Emacs,Java,ergp,less,more,.NET语言,PCRE library,Perl,PHP,Python,Ruby,sed,vi;
使用POSIX NFA引擎的程序主要有:mawk,Mortice Kern Systems’ utilities,GNU Emacs(使用时可以明确指定);
也有使用DFA/NFA混合的引擎:GNU awk,GNU grep/egrep,Tcl。
举例简单说明NFA与DFA工作的区别:
比如有字符串this is yansen’s blog,正则表达式为 /ya(msen|nsen|nsem)/ (不要在乎表达式怎么样,这里只是为了说明引擎间的工作区别)。 NFA工作方式如下,先在字符串中查找 y 然后匹配其后是否为 a ,如果是 a 则继续,查找其后是否为 m 如果不是则匹配其后是否为 n (此时淘汰msen选择支)。然后继续看其后是否依次为 s,e,接着测试是否为 n ,是 n 则匹配成功,不是则测试是否为 m 。为什么是 m ?因为 NFA 工作方式是以正则表达式为标准,反复测试字符串,这样同样一个字符串有可能被反复测试了很多次!
而DFA则不是如此,DFA会从 this 中 t 开始依次查找 y,定位到 y ,已知其后为a,则查看表达式是否有 a ,此处正好有a 。然后字符串a 后为n ,DFA依次测试表达式,此时 msen 不符合要求淘汰。nsen 和 nsem 符合要求,然后DFA依次检查字符串,检测到sen 中的 n 时只有nsen 分支符合,则匹配成功!
由此可以看出来,两种引擎的工作方式完全不同,一个(NFA)以表达式为主导,一个(DFA)以文本为主导!一般而论,DFA引擎则搜索更快一些!但是NFA以表达式为主导,反而更容易操纵,因此一般程序员更偏爱NFA引擎! 两种引擎各有所长,而真正的引用则取决与你的需要以及所使用的语言!

特殊字符

1
2
(转义字符)若要匹配这些特殊字符之一,在字符前面加反斜杠字符 (\)。 
例如,若要搜索“+”文本字符,可使用表达式“\+”。

元字符 行为 示例 /* 零次或多次匹配前面的字符或子表达式。
等效于 {0,}。 zo/* 与“z”和“zoo”匹配。 + 一次或多次匹配前面的字符或子表达式。
等效于 {1,}。 zo+ 与“zo”和“zoo”匹配,但与“z”不匹配。 ? 零次或一次匹配前面的字符或子表达式。
等效于 {0,1}。
当 ? 紧随任何其他限定符(/*、+、?、{n}、{n,} 或 {n,m})之后时,匹配模式是非贪婪的。
非贪婪模式匹配搜索到的、尽可能少的字符串, 而默认的贪婪模式匹配搜索到的、
尽可能多的字符串。 zo+ 与“zo”和“zoo”匹配,但与“z”不匹配。 ^ 匹配搜索字符串开始的位置。 如果标志中包括 m(多行搜索)字符,^ 还将匹配 \n 或 \r 后面的位置。
如果将 ^ 用作括号表达式中的第一个字符,则会对字符集求反。 ^\d{3} 与搜索字符串开始处的 3 个数字匹配。
[^abc] 与除 a、b 和 c 以外的任何字符匹配。 $ 匹配搜索字符串结尾的位置。 如果标志中包括 m(多行搜索)字符,^ 还将匹配 \n 或 \r 前面的位置。 \d{3}$ 与搜索字符串结尾处的 3 个数字匹配。 . 匹配除换行符 \n 之外的任何单个字符。 若要匹配包括 \n 在内的任意字符,请使用诸如 [\s\S] 之类的模式。 a.c 与“abc”、“a1c”和“a-c”匹配。 [] 标记括号表达式的开始和结尾。 [1-4] 与“1”、“2”、“3”或“4”匹配。 [^aAeEiIoOuU] 与任何非元音字符匹配。 {} 标记限定符表达式的开始和结尾。 a{2,3} 与“aa”和“aaa”匹配。 () 标记子表达式的开始和结尾。 可以保存子表达式以备将来之用。 A(\d) 与“A0”至“A9”匹配。 保存该数字以备将来之用。 | 指示在两个或多个项之间进行选择。 z|food 与“z”或“food”匹配。 (z|f)ood 与“zood”或“food”匹配。 / 表示 JScript 中的文本正则表达式模式的开始或结尾。
在第二个“/”后添加单字符标志可以指定搜索行为。 /abc/gi 是与“abc”匹配的 JScript 文本正则表达式。
g(全局)标志指定查找模式的所有匹配项,i(忽略大小写)标志使搜索不区分大小写。 | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。 \n 与换行符匹配。 ( 与“(”匹配。 \ 与“”匹配。

元字符

1
下表包含了多字符元字符的列表以及它们在正则表达式中的行为。

元字符 行为 示例 \b 与一个字边界匹配;即字与空格间的位置。 er\b 与“never”中的“er”匹配,但与“verb”中的“er”不匹配。 \B 非边界字匹配。 er\B 与“verb”中的“er”匹配,但与“never”中的“er”不匹配。 \d 数字字符匹配。等效于 [0-9]。 在搜索字符串“12 345”中,\d{2} 与“12”和“34”匹配。 \d 与“1”、“2”、“3”、“4”和“5”匹配。 \D 非数字字符匹配。等效于 [^0-9]。 \D+ 与“abc123 def”中的“abc”和“def”匹配。 \w 与以下任意字符匹配:A-Z、a-z、0-9 和下划线。等效于 [A-Za-z0-9_]。 在搜索字符串“The quick brown fox…”中,\w+ 与“The”、“quick”、“brown”和“fox”匹配。 \W 与除 A-Z、a-z、0-9 和下划线以外的任意字符匹配。等效于 [^A-Za-z0-9_]。 在搜索字符串“The quick brown fox…”中,\W+ 与“…”和所有空格匹配。 [xyz] 字符集。 与任何一个指定字符匹配。 [abc] 与“plain”中的“a”匹配。 [^xyz] 反向字符集。 与未指定的任何字符匹配。 [^abc] 与“plain”中的“p”、“l”、“i”和“n”匹配。 [a-z] 字符范围。 匹配指定范围内的任何字符。 [a-z] 与“a”到“z”范围内的任何小写字母字符匹配。 [^a-z] 反向字符范围。 与不在指定范围内的任何字符匹配。 [^a-z] 与不在范围“a”到“z”内的任何字符匹配。 {n} 正好匹配 n 次。 n 是非负整数。 o{2} 与“Bob”中的“o”不匹配,但与“food”中的两个“o”匹配。 {n,} 至少匹配 n 次。 n 是非负整数。
/* 与 {0,} 相等。

  • 与 {1,} 相等。 o{2,} 与“Bob”中的“o”不匹配,但与“foooood”中的所有“o”匹配。 {n,m} 匹配至少 n 次,至多 m 次。 n 和 m 是非负整数,其中 n <= m。 逗号和数字之间不能有空格。
    ? 与 {0,1} 相等。 在搜索字符串“1234567”中,\d{1,3} 与“123”、“456”和“7”匹配。

非打印字符

字符 匹配 等效于 \f 换页符。 \x0c 和 \cL \n 换行符。 \x0a 和 \cJ \r 回车符。 \x0d 和 \cM \s 任何空白字符。 其中包括空格、制表符和换页符。 [ \f\n\r\t\v] \S 任何非空白字符。 [^ \f\n\r\t\v] \t Tab 字符。 \x09 和 \cI \v 垂直制表符。 \x0b 和 \cK

优先级顺序

1
2
正则表达式的计算方式与算术表达式非常类似;即从左到右进行计算,并遵循优先级顺序。
下表按从高到低的顺序包含了正则表达式运算符的优先级顺序。

运算符 说明 \ 转义符 (), (?:), (?=), [] 括号和中括号 /*、+、?、{n}、{n,}、{n,m} 限定符 ^、$、\任何元字符 定位点和序列 | 替换

在线校验

https://regex101.com/