简友广场想法

正则表达式

2020-01-11  本文已影响0人  相忘于江湖eyu

是什么?

正则表达式就是用于描述查找字符串符合的某些复杂规则的工具。换句话说,正则表达式就是记录文本规则的代码。是用来进行文本匹配的工具。不同的环境下正则表达式的一些细节是不相同的

基本概念

元字符(metacharacter)

注: 和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。

字符转义

使用\来取消这些字符的特殊意义,如:\.、\*、\\等

重复

正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):

字符类

如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。

也可以指定范围,如:
[0-9]代表的含意与\d就是完全一致的:一位数字;
同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。

分支条件

指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开,如:

分组

用小括号来指定子表达式(也叫做分组)

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义

简单例子

常用分组语法

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。例如:\b(\w+)\b\s+\1\b
你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b。

零宽断言

用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言

负向零宽断言

\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b

我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

一个更复杂的例子:(?<=<(\w+)>).(?=</\1>)*匹配不包含属性的简单HTML标签内里的内容

注释

(?#comment),要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。例如:

 (?<=    # 断言要匹配的文本的前缀
 <(\w+)> # 查找尖括号括起来的内容
         # (即HTML/XML标签)
 )       # 前缀结束
 .*      # 匹配任意文本
 (?=     # 断言要匹配的文本的后缀
 <\/\1>  # 查找尖括号括起来的内容
         # 查找尖括号括起来的内容
 )       # 后缀结束

其他

贪婪与懒惰

注:为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。

懒惰限定符

处理选项

如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。常见的有:

平衡组/递归匹配

如何把xx <aa <bbb> <bbb> aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来?

用到以下的语法构造:

我们需要做的是每碰到了左括号,就在压入一个"Open",每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。

 <                   #最外层的左括号
   [^<>]*            #它后面非括号的内容
   (
       (
         (?'Open'<)  #左括号,压入"Open"
         [^<>]*      #左括号后面的内容
       )+
       (
         (?'-Open'>) #右括号,弹出一个"Open"
         [^<>]*      #右括号后面的内容
       )+
   )*
   (?(Open)(?!))     #最外层的右括号前检查
                     #若还有未弹出的"Open"
                     #则匹配失败
 
 >                #最外层的右括号

正则表达式元素一览

也可查看关于正则表达式语言元素的MSDN在线文档

补充

主流的正则引擎又分为3类:一、DFA,二、传统型NFA,三、POSIX NFA。
两种引擎的工作方式完全不同,一个(NFA)以表达式为主导,一个(DFA)以文本为主导!一般而论,DFA引擎则搜索更快一些!但是NFA以表达式为主导,反而更容易操纵,因此一般程序员更偏爱NFA引擎! 两种引擎各有所长,而真正的引用则取决与你的需要以及所使用的语言

参考资料

上一篇 下一篇

猜你喜欢

热点阅读