转自:http://book.51cto.com/art/201104/259073.htm

第3章 正则表达式

Perl的正则表达式本身就相当于一门语言了,而且这门语言甚至比Perl更复杂。

我们不会用到正则表达式的全部特性,但其中有些特性的确能大大简化日常工作。本章介绍其中较为常用的一部分特性。

尽管为了提升效率,开发人员已经对正则表达式引擎内部作了大量优化,但即使经验丰富的开发人员,也偶尔会写出效率极为低下的匹配和替换表达式。

效率并不总是我们的首要目标。事实上,在软件开发过程中,效率不应该是我们的首要目标。通常来说,程序员的首要任务是提供完备、稳定而正确的解决方案。当然在开发时,时刻注意效率问题总还是没有坏处的。

由于现在Perl已经能够处理Unicode字符,所以Perl的正则表达式同样能够处理字节(byte)、字符(character)和字素(grapheme)。不仅如此,正则表达式还可以处理字符属性(character property)。我们把大部分此类问题保留到第8章再详细讨论,也就是说,第8章也将包含不少与正则表达式相关的内容。

条款28 了解正则表达式操作符的优先级(1)

"正则表达式"一词中之所以包含"表达式",是因为构成和解析正则表达式的语法近似于算术表达式。诚然,正则表达式与算术表达式的作用各不相同,但理解二者的相似性,有助于写出更严谨的正则表达式,或者说更完美的Perl程序。

正则表达式由原子和操作符组成。原子(atom)是构成正则表达式的基本单位,通常是指仅匹配单个字符的匹配模式。例如:

 
 
  1. a#匹配字母a
  2. \$#匹配字符$
  3. \n#匹配换行符
  4. [a-z]#匹配任何一个小写字母
  5. .#匹配除\n以外的任意字符
  6. \1#反向引用所匹配到的第一组捕获内容(文本长度不限)

此外还有一些特殊的"零宽度"原子 ,例如:

  
  
  • \b#单词边界,从\w转换到\W的分界点,反之亦然
  • ^#匹配字串行首位置
  • \A#字串的绝对行首
  • \Z#字串末尾位置或换行符位置
  • #可能是也可能不是零宽度
  • \z#绝对行尾位置,之后再无其他内容
  • 原子由正则表达式操作符修饰或联结在一起。与算术表达式相似,正则表达式的操作符之间也是有优先级次序的。

    1. 正则表达式的优先级

    幸运的是,正则表达式只有四层优先级。试想一下如果正则表达式的优先级和数学表达式一样多一样复杂,会是怎样一种情况!

    圆括号和其他分组操作符拥有最高优先级。表3-1列出了正则表达式操作符的优先级。

    量词与它所修饰的元素的结合最为紧密,不论所修饰的是原子还是分组:

      
      
  • ab*c#匹配ac、abc、abbc、abbbc等
  • abc*#匹配ab、abc、abcc、abccc等
  • ab(c)*#同上,并捕获字母c
  • ab(?:c)*#同上,但不捕获字母c
  • abc{2,4}#匹配abcc、abccc、abcccc
  • (abc)*#匹配空字串、abc、abcabc等
  • 表3-1 正则表达式操作符优先级次序(从高到低)

    优先次序

    操 作 符

    描述

    最高级

    () (?:),等等

    圆括号或其他分组操作符

    ? + * {m,n} +? ++,等等

    重复次数

    ^ $ abc \G \b \B [abc]

    字符序列、文字字符、

    字符组、断言

    最低级

    a|b

    多选结构

    两个原子顺次排列称之为序列(sequence)。虽然没用标点符号,但序列也是一种操作符。为清晰起见,下文使用圆点(o)表示序列关系。上述例子因此变为:

      
      
  • aob*oc#匹配ac、abc、abbc、abbbc等
  • aoboc*#匹配ab、abc、abcc、abccc等
  • aobo(c)*#同上,同时捕获字母c
  • aobo(?:c)*#同上,但不捕获字母c
  • aoboc{2,4}#匹配abcc、abccc、abcccc
  • (aoboc)*#匹配空字串、abc、abcabc等
  • 现在操作符之间的优先级关系是不是明显许多了?

    在优先级次序中级别最低的,要数多选结构了。下面继续使用o记号提示:

      
      
  • eod|joo#匹配ed或jo
  • (eod)|(joo)#同上
  • eo(d|j)oo#匹配edo或ejo
  • eod|joo{1,3}#匹配ed、jo、joo、jooo
  • 像^或\b这样的零宽度原子,与其他原子的优先次序是一致的:
  • ^eod|joo$#匹配行首的ed,或行尾的jo
  • ^(eod|joo)$#匹配仅含ed或仅含jo的行
  • 优先级次序不太好记。剔除多余括号属于高级技巧,在正则表达式中尤其如此,因而特别需要留意,以防删除过多而误事:

      
      
  • #这封邮件是谁发给我的?
  • /^Sender|From:\s+(.*)/;#错误!它可以匹配像这样的伪造头:
  • #X-Not-Really-From:faker
  • 上述模式的本意是要匹配邮件头中Sender:或From:开头的行,但实际上能匹配到的内容显然不是我们所期望的。如果加上合适的括号,意思就清晰了:

      
      
  • /(^Sender)|(From:\s+(.*))/;
  • 添加一对圆括号,或非捕获型括号(?:)(见条款32),问题就能迎刃而解:

      
      
  • #改良版本
  • /^(Sender|From):\s+(.*)/;#变量$1保存的是单词Sender或From
  • /^(?:Sender|From):\s+(.*)/;#变量$1保存的是真正要提取的内容
  • 2. 双引号变量内插

    Perl正则表达式的变量插值方式与双引号字串内的变量插值方式相同。变量名称以及\U和\Q这样的字符转义,并不属于正则表达式原子,因而正则表达式解析器不会处理它们。内插是一个单独的过程,发生在Perl解析正则表达式之前:

     
     
    1. /te(st)/;#测试系统变量$_是否匹配test
    2. /\Ute(st)/;#匹配TEST
    3. /\Qte(st)/;#匹配te(st)
    4. $x='test';
    5. /$x*/;#匹配tes、test、testt等
    6. /test*/;#同上


    不明白变量插值和正则表达式解析过程的先后顺序,常常会导致混淆误用的情况。请看下面的例子,要是把插值变量当成了正则表达式的原子,会发生什么情况:

      
      
  • #读入一个模式,匹配该模式出现两次的情形
  • chop($pat=<STDIN>);#例如,读入的模式为bob
  • print"matched\n"if/$pat{2}/;#错误,实际相当于/bob{2}/
  • print"matched\n"if/($pat){2}/;#正确,实际相当于/(bob){2}/
  • print"matched\n"if/$pat$pat/;#简单罗列式,虽然相等,倒也正确
  • 上例中,如果用户输入bob,第一条正则表达式所匹配的就是bobb,因为在解析正则表达式之前,变量$pat已经被实际内容替换了。

    上面三条正则表达式都隐藏着另一个陷阱。假如用户输入的是字串hello :-),就会导致严重的运行时错误。变量内插后得到的实际正则表达式会从/($pat){2}/变为/(hello :-)){2}/,不光是毫无意义,连括号也不对称了。Perl会报告正则表达式错在哪里:

      
      
  • Unmatched)inregex;markedby--HEREin
  • m/(hello:-))--HERE{2}/
  • 这种问题我们可以用qr//解决(见条款40)。

    对于括号、星号、点号之类的特殊字符,如果不想把它们作为正则表达式元字符使用,可以借助quoteMeta操作符,或是转义操作符\Q。quoteMeta和\Q会在任何不是字母、数字及下划线的字符之前加上反斜线作转义处理。

    下面的代码从标准输入读取字串,使用quoteMeta将字符中的特殊字符转义,然后再用于匹配。如果输入仍旧是hello :-),那么转义后会变为hello\ \:\-\),可以放心地用这个正则表达式进行匹配了。

      
      
  • chomp($pat=>);
  • my$quoted=quoteMeta$pat;
  • print"matched\n"if/($quoted){2}/;
  • 或者,直接在表达式中使用转义操作符\Q和\E :

    >); 
      
      
  • print"matched\n"if/(\Q$pat\E){2}/;
  • 跟使用正则表达式的其他语法结构一样,一点点疏忽都可能造成正则表达式崩溃:

      
      
  • #它实际上相当于/hello\\:\-\)\{2\}/,显然这是一个致命错误
  • print"matched\n"if/(\Q$pat){2}/;#错误!漏掉了\E
  • 3. 要点

    要留意正则表达式的优先级。

    可以使用圆括号将正则表达式分组。

    使用\Q或quoteMeta将元字符转义为普通字符。

    了解正则表达式操作符的优先级的更多相关文章

    1. HTML5数字输入仅接受整数的实现代码

      这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

    2. ios – 使用大写符号在字符串swift中获取URL的正则表达式

      我尝试在文本中获取URL.所以,在此之前,我使用了这样一个表达式:但是当用户输入带有大写符号的URL时(例如Http://Google.com,它与它不匹配)我遇到了问题.我试过了:但什么都没发生.解决方法您可以使用正则表达式中的i内联标志关闭区分大小写,有关可用正则表达式功能的详细信息,请参阅FoundationFrameworkReference.(?ismwx-ismwx)Flagsetti

    3. iOS推送通知优先级

      我已设置推送通知并正常工作,但是,有时我会遇到终端设备上的延迟交付.有没有办法我可以将推送的“优先级”键设置为10,以便立即发送推送?

    4. ios – 何时使用Semaphore而不是Dispatch Group?

      我会假设我知道如何使用DispatchGroup,为了解问题,我尝试过:结果–预期–是:为了使用信号量,我实现了:并在viewDidLoad方法中调用它.结果是:从概念上讲,dispachGroup和Semaphore都有同样的目的.老实说,我不熟悉:什么时候使用信号量,尤其是在与dispachGroup合作时–可能–处理问题.我错过了什么部分?

    5. 如何使用Xcode的自动布局调整视图大小

      解决方法在写这个问题时,我意识到了诀窍是什么:在NSPopUpButton的大小检查器中,我不得不降低内容拥抱优先级.显然,这可以控制视图“拥抱”其内容的紧密程度.因此,当拥抱优先级高于调整大小优先级时,视图将不希望增加其大小,因为这意味着其边界与其内容之间具有更多的空白空间.然后在我的特殊情况下,我也可以将两个NSPopUpButtons固定为具有相同的宽度和vo:popUpButtons将完美地调整大小,同时保持间距不变.

    6. ios – 默认的自动布局内容拥抱和内容压缩阻抗优先级值是什么?

      我正在尝试调试自动布局问题,并且知道内容拥抱和内容压缩阻力优先级的默认值将有所帮助.这些是什么?它们是否特定于特定组件?我可以使用常量来引用它们吗?

    7. ios – 如何在Swift 3中使用正则表达式?

      解决方法我相信.当没有其他选项适用时,将使用.allZeros.因此,使用Swift3,您可以传递一个空的选项列表或省略options参数,因为它默认为无选项:要么请注意,在Swift3中,您不再使用error参数.它现在抛出.

    8. ios – lldb断点在类目标c中的所有方法

      如何使用lldb在ObjectiveC类中的所有方法上自动设置断点?

    9. ios – 为自定义创建的串行异步队列设置优先级

      如何使用GCD为自定义创建的串行异步队列设置高优先级?如果是这样,什么是替代解决方案?解决方法您的队列仍然是串行的.它只会在高优先级全局并发后台队列的一个插槽中一次执行一项任务.一旦创建,串行队列就不能以任何方式“并发”.同样,如果您创建并发队列并将其设置为以串行队列为目标,则它实际上变为串行.这一切都在manpage中有所涉及.

    10. iOS 9中UILabel中的多行文本

      我正在开发iOS项目但是当我更新到iOS9时,我在UILabels中遇到了多线问题.我正在使用Autolayout.有谁知道如何在iOS9中做到这一点?如果是这样,那么问题可能是标签内容压缩阻力优先级太低,尝试将其设置为required或1000.内容压缩阻力告诉视图引擎您的标签可以缩小的优先级.将其设置为必需会强制它不缩小.在InterfaceBuilder中,只需选择标签,点击SizeInspector(小标尺),然后将其更改为1000.或者,在代码中,等价物将是:

    随机推荐

    1. 法国电话号码的正则表达式

      我正在尝试实施一个正则表达式,允许我检查一个号码是否是一个有效的法国电话号码.一定是这样的:要么:这是我实施的但是错了……

    2. 正则表达式 – perl分裂奇怪的行为

      PSperl是5.18.0问题是量词*允许零空间,你必须使用,这意味着1或更多.请注意,F和O之间的空间正好为零.

    3. 正则表达式 – 正则表达式大于和小于

      我想匹配以下任何一个字符:或=或=.这个似乎不起作用:[/]试试这个:它匹配可选地后跟=,或者只是=自身.

    4. 如何使用正则表达式用空格替换字符之间的短划线

      我想用正则表达式替换出现在带空格的字母之间的短划线.例如,用abcd替换ab-cd以下匹配字符–字符序列,但也替换字符[即ab-cd导致d,而不是abcd,因为我希望]我如何适应以上只能取代–部分?

    5. 正则表达式 – /bb | [^ b] {2} /它是如何工作的?

      有人可以解释一下吗?我在t-shirt上看到了这个:它似乎在说:“成为或不成为”怎么样?我好像没找到’e’?

    6. 正则表达式 – 在Scala中验证电子邮件一行

      在我的代码中添加简单的电子邮件验证,我创建了以下函数:这将传递像bob@testmymail.com这样的电子邮件和bobtestmymail.com之类的失败邮件,但是带有空格字符的邮件会漏掉,就像bob@testmymail也会返回true.我可能在这里很傻……当我测试你的正则表达式并且它正在捕捉简单的电子邮件时,我检查了你的代码并看到你正在使用findFirstIn.我相信这是你的问题.findFirstIn将跳转所有空格,直到它匹配字符串中任何位置的某个序列.我相信在你的情况下,最好使用unapp

    7. 正则表达式对小字符串的暴力

      在测试小字符串时,使用正则表达式会带来性能上的好处,还是会强制它们更快?不会通过检查给定字符串的字符是否在指定范围内比使用正则表达式更快来强制它们吗?

    8. 正则表达式 – 为什么`stoutest`不是有效的正则表达式?

      isthedelimiter,thenthematch-only-onceruleof?PATTERN?

    9. 正则表达式 – 替换..与.在R

      我怎样才能替换..我尝试过类似的东西:但它并不像我希望的那样有效.尝试添加fixed=T.

    10. 正则表达式 – 如何在字符串中的特定位置添加字符?

      我正在使用记事本,并希望使用正则表达式替换在字符串中的特定位置插入一个字符.例如,在每行的第6位插入一个逗号是什么意思?如果要在第六个字符后添加字符,请使用搜索和更换从技术上讲,这将用MatchGroup1替换每行的前6个字符,后跟逗号.

    返回
    顶部