正则表达式

作者: 相忘于江湖eyu | 来源:发表于2020-01-11 10:18 被阅读0次

是什么?

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

基本概念

元字符(metacharacter)

  • \b:代表着单词的开头或结尾,也就是单词的分界处,它并不匹配单词分隔字符中的任何一个,它只匹配一个位置。\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w
  • .:匹配除了换行符以外的任意字符
  • *:同样是元字符,不过它代表是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配;.*连在一起就意味着任意数量的不包含换行的字符
  • \d:匹配一位数字(0~9)
  • \s:匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等
  • \w:匹配字母或数字或下划线或汉字等
  • +:和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次
  • ^:匹配字符串的开始
  • $:匹配字符串的结束

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

字符转义

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

重复

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

  • * 重复零次或更多次
  • + 重复一次或更多次
  • ? 重复零次或一次
  • {n} 重复n次
  • {n,} 重复n次或更多次
  • {n,m} 重复n到m次

字符类

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

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

分支条件

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

  • 0\d{2}-\d{8}|0\d{3}-\d{7}
  • (0\d{2})[- ]?\d{8}|0\d{2}[- ]?\d{8}
    使用分枝条件时,要注意各个条件的顺序。,如\d{5}-\d{4}|\d{5},如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

分组

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

反义

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

  • \W 匹配任意不是字母,数字,下划线,汉字的字符
  • \S 匹配任意不是空白符的字符
  • \D 匹配任意非数字的字符
  • \B 匹配不是单词开头或结束的位置
  • [^x] 匹配除了x以外的任意字符
  • [^aeiou] 匹配除了aeiou这几个字母以外的任意字符

简单例子

  • \bhi\b.*\bLucy\b
  • 0\d{2}-\d{8}
  • \ba\w*\b
  • \d+
  • \b\w{6}\b
  • ^\d{5,12}$
  • (?0\d{2}[) -]?\d{8}
  • (\d{1,3}.){3}\d{1,3}

常用分组语法

  • 捕获
    • (exp):匹配exp,并捕获文本到自动命名的组里
    • (?<name>exp):匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
    • (?:exp):匹配exp,不捕获匹配的文本,也不给此分组分配组号
  • 零宽断言
    • (?=exp):匹配exp前面的位置
    • (?<=exp):匹配exp后面的位置
    • (?!exp):匹配后面跟的不是exp的位置
    • (?<!exp):匹配前面不是exp的位置
  • 注释
    • (?#comment):这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为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。

  • 分组0对应整个正则表达式
  • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
  • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

零宽断言

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

  • (?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。
  • (?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。
    例如:((?<=\d)\d{3})+\b用它对1234567890进行查找时结果是234567890。

负向零宽断言

\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>  # 查找尖括号括起来的内容
         # 查找尖括号括起来的内容
 )       # 后缀结束

其他

贪婪与懒惰

  • 贪婪匹配:当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符,如:a.*b匹配aabab得到aabab
  • 懒惰匹配:也就是匹配尽可能少的字符a.*?b应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

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

懒惰限定符

  • *? 重复任意次,但尽可能少重复
  • +? 重复1次或更多次,但尽可能少重复
  • ?? 重复0次或1次,但尽可能少重复
  • {n,m}? 重复n到m次,但尽可能少重复
  • {n,}? 重复n次以上,但尽可能少重复

处理选项

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

  • IgnoreCase(忽略大小写):匹配时不区分大小写。
  • Multiline(多行模式):更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)
  • Singleline(单行模式):更改.的含义,使它与每一个字符匹配(包括换行符\n)。(注:单行模式其实名叫 dotAll,意为点可以匹配所有字符)
  • IgnorePatternWhitespace(忽略空白):忽略表达式中的非转义空白并启用由#标记的注释。
  • ExplicitCapture(显式捕获):仅捕获已被显式命名的组。

平衡组/递归匹配

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

用到以下的语法构造:

  • (?'group'): 把捕获的内容命名为group,并压入堆栈(Stack)
  • (?'-group'): 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
  • (?(group)yes|no): 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
  • (?!): 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

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

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

正则表达式元素一览

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

  • \a: 报警字符(打印它的效果是电脑嘀一声)
  • \b: 通常是单词分界位置,但如果在字符类里使用代表退格
  • \t: 制表符,Tab
  • \r: 回车
  • \v: 竖向制表符
  • \f: 换页符
  • \n: 换行符
  • \e: Escape
  • \0nn: ASCII代码中八进制代码为nn的字符
  • \xnn: ASCII代码中十六进制代码为nn的字符
  • \unnnn: Unicode代码中十六进制代码为nnnn的字符
  • \cN: ASCII控制字符。比如\cC代表Ctrl+C
  • \A: 字符串开头(类似^,但不受处理多行选项的影响)
  • \Z: 字符串结尾或行尾(不受处理多行选项的影响)
  • \z: 字符串结尾(类似$,但不受处理多行选项的影响)
  • \G: 当前搜索的开头
  • \p{name}: Unicode中命名为name的字符类,例如\p{IsGreek}
  • (?>exp): 贪婪子表达式
  • (?<x>-<y>exp): 平衡组
  • (?im-nsx:exp): 在子表达式exp中改变处理选项
  • (?im-nsx): 为表达式后面的部分改变处理选项
  • (?(exp)yes|no): 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no
  • (?(exp)yes): 同上,只是使用空表达式作为no
  • (?(name)yes|no): 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no
  • (?(name)yes): 同上,只是使用空表达式作为no

补充

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

参考资料

相关文章

  • Linux命令行与Shell脚本编程大全-shell正则表达式

    本章内容: 定义正则表达式 了解基本正则表达式 扩展正则表达式 创建正则表达式 定义正则表达式 正则表达式是你定义...

  • 正则相关

    正则表达式基本语法 正则表达式常见字符 正则表达式特殊字符 正则表达式数量词 正则表达式边界匹配 正则表达式逻辑或...

  • 正则表达式系列-1

    正则表达式系列-1正则表达式系列-2正则表达式系列-3正则表达式系列-4 什么是正则表达式 正则表达式就是用事先定...

  • 正则表达式

    正则表达式 - 教程正则表达式 - 简介正则表达式 - 语法正则表达式 - 元字符正则表达式 - 运算符优先级正则...

  • Python基础入门 - 正则表达式与综合实战

    1. 初识正则表达式 1.1 介绍 步骤介绍正则表达式入门及应用正则表达式的进阶正则表达式案例 1.2 正则表达式...

  • Java正则表达式参考

    Java正则表达式入门 java正则表达式应用 深入浅出之正则表达式(一) 深入浅出之正则表达式(二) 正则表达式...

  • 正则表达式

    正则表达式 正则表达式就是记录文本规则的代码 正则表达式常用的元字符 正则表达式常用的限定符 正则表达式举例:这里...

  • Python爬虫(十)_正则表达式

    本篇将介绍python正则表达式,更多内容请参考:【python正则表达式】 什么是正则表达式 正则表达式,又称规...

  • python正则表达式

    本篇将介绍python正则表达式,更多内容请参考:【python正则表达式】 什么是正则表达式 正则表达式,又称规...

  • 正则表达式

    了解正则表达式基本语法 能够使用JavaScript的正则对象 正则表达式简介 什么是正则表达式 正则表达式:用于...

网友评论

    本文标题:正则表达式

    本文链接:https://www.haomeiwen.com/subject/qwkbmqtx.html