学习正则表达式

暑假回家的路上,断断续续的看完《学习正则表达式》这本书,选择这本主要是因为书确实比较薄(142页),快速入门很不错,后期需要深入研究的话,再结合大部头的《精通正则表达式》或者《学习正则表达式》就很不错。于是记录记录,加深印象。

先直接上一段正则表达式,如果我们要在一大段文本中找出带区号的(大陆的)电话号码,比如“这是我家的电话:0277-6184073,其实这个才是啦:027-77777777”这段话,我们就可以这样写正则表达式:0[1-9]{2,3}-\d{7,8} 。大家可以打开RegExr网站 试一下,会发现两段电话号码被高亮显示。
这里的第一个0被称作字符串字面值(string litera)。直接匹配电话号码的第一个字符0(大陆地区的区号均以0开头),后面出现的 - 也是字符串字面值。

方括号[和]被称作元字符(metacharacter),是正则表达式中有特殊意义的字符,不参与实际匹配,也是保留字符。作用是匹配方括号中出现的字符集合中的任意一个字符,[1-9]这种形式的正则表达式被称作字符组(character class),有时也叫做字符集(character set)(PS. 其实感觉应该指的是1-9,而不包括方括号)。这里表示匹配1至9这九个数中的任意一个。类似的还有[a-z],[_a-zA-Z0-9] , 后者表示数字,字母和下划线三者中的任意一个。

花括号{和}被称作量词,和方括号[ ,]一样是元字符,不参与实际匹配,其中的数字表示待查找的前面那个对象出现的次数,比如a{2}就是连续两次出现a,a{2,3}就是连续出现两次或三次a。其他常用的量词还有 ? , * , +等,分别表示”零个或一个”,”零个或多个”,”一个或多个” 。这里比较容易混淆的就是点号. ,它 是一个通配符,不是量词,能够匹配任意字符(某些情况不能匹配行起始符,被称作dotall选项)。

而\d 被称作字符组简写式(character shorthand),也叫转义字符(character escape)。这里 \d 等同于[0-9],所以字符组简写式也由此得名。类似的还有 \w ,等同于[_a-zA-Z0-9] ,而\D 和 \W 一般表示的意义和其对应的 \d 以及 \w 意义一般是相反的,后面会有详细介绍。

简单模式匹配(匹配数字,字符串,空白符和边界等)

[0-9] 、[^0-9]以及 ^\d , 这里的脱字符^是取反的意思。而\D(等价于^\d)会匹配空格,标点符号和 \w 所匹配的内容。
常用字符简写式表:
常用字符简写式表
最常用的匹配空白符的表达式是 \s ,相当于[ \t\n\r] 。
常用空白符简写式表:
常用空白符简写式表

对于匹配边界,\b 表示匹配单词的开始和结束,^和$分别匹配行的开头和结尾,而^$就是表示匹配空白行了。举个例子,\b7\b 匹配出的7,其左右都不会出现[_A-Za-z0-9]字符,而\B7\B则匹配出左右均是[_A-Za-z0-9]中的某个字符的7。

这里有必要提一下元字符的字面值: .^$+?|(){}[]- 这15个元字符在正则表达式中有特殊含义(连字符-只在字符组的方括号中用来表示范围,在其他情况下,则无特殊含义),如果要匹配文本中上述的某个字符,需要用类似于 \Q$\E 的形式,因为\Q和\E之间的任意字符都会被解释为普通字符。

选择分组和后向引用

选项

正则表达式中的常用选项:
正则表达式常用选项
下面分别以grep,perl命令为例:
grep -Ec “(the|The|THE)” test.txt
(-E表示使用扩展的ERE,而不是BRE,如果使用BRE则需要对括号进行转义;
-c选项返回匹配的行数,而不是单词,括号对选择操作分组)
grep -Eo “(the|The|THE)” test.txt | wc -l
(-o选项表示只显示一行中与指定模式匹配的部分;
-wc是单词计数的命令,而-l是对输入的行数统计)
对于perl执行同种匹配,则这样写:
perl -ne ‘print if /(the|The|THE/)’ test.txt 或者
perl -ne ‘print if /(?i)the/‘ test.txt 或者
perl -ne ‘print if /the/i’ test.txt

子模式(subpattern),多数情况指的是分组中的一个或多个分组

某种意义上(the|The|THE)有三个子模式,只是不相互依赖,而(t|T)h(e|eir)中子模式依赖于前面的模式,等同于下面的字符组(子模式):\b[tT]h[ceinry]*\b

捕获分组和后向引用

由一对括号捕获分组, 由\1,\2 或者$1,$2后向引用,如 (It is)(a nice day)捕获后,用\u$2\l$1引用,则可以得到 A nice day it is.
命名分组: (?It is)(?a nice day)捕获,引用:\u$+{two} \l${one} 。
命名分组的语法:
命名分组的语法

非捕获分组(Non-Capturing Group).

非捕获分组不会将其内容存储在内存中,在你不想引用分组的时候,可以使用这个。比如(?i:the),该选项i可以房子啊问号和冒号之间。
原子分组: 这种分组可以将回溯操作关闭。但只针对原子分组内部的部分,语法为(?>the).在正则表达式处理过程缓慢的一个因素就是回溯,re2这样的非回溯引擎可彻底关闭回溯操作。

字符组(有时也被成为方括号表达式)

  • 匹配 0-99的偶数:\b[24690]\b|\b[1-9][24680]\b, 匹配罗马数字:I{0,3}V?I{0,2}
  • 并集与差集: 两个字符组的并集,[0-3[6-9]],匹配差集,[a-z&&[^m-r]]表示匹配a-z之间的字符,但是m到r之间的除外。
  • 匹配Unicode和其他字符。/x用来匹配0-FF之间的字符,/u用来匹配100-FFFF之间的四位十六进制数,U用来匹配10000-7FFFFFFF范围内的八位十六进制数。

匹配Unicode及其他字符表:
匹配Unicode及其他字符表

量词

贪心、懒惰和占有

  • 贪心 量词自身是贪心的,会首先匹配整个字符串。如果失败则会退回一个字符后再次尝试,这个过程叫做回溯(backtracking),此外,它还会记录所有的行为。
  • 懒惰的量词则从目标的起始位置开始尝试需找匹配,每次检查字符串的一个字符,要使一个量词成为懒惰的,必须在普通量词后添加一个问号(?).
  • 占有量词会覆盖整个目标然后尝试寻找匹配内容,但它只尝试一次,不会回溯。在普通量词之后添加一个加号(+)。
    例子:
  • 懒惰 对于 7? 会匹配0个或1和7,而 7??或者7? 则不会匹配任何内容,7+?或者 7{1,7}则只匹配一个7 。
  • 占有 7.+会匹配所有的7(Match All),而.*+7则什么都不会匹配,原因就在于没有回溯。

环视(也称零宽度断言)

  • 正前瞻
    正前瞻(?i)lucky (?=dog)表示每一行中寻找后跟dog的单词lucky,但只有模式的第一部分被标亮。环视模式不会标亮。
  • 反前瞻
    反前瞻意味着要匹配某个模式时,需要在它后面找不到含有给定前瞻模式的内容,形式是: (?i)lucky (?!dog)
  • 正后顾
    正后顾会查看左边的内容,与正前瞻相反,语法是: (?i)(?<=lucky) dog ,会标亮左边有lucky的dog
  • 反后顾
    反后顾语法为:(?i)(?<!lucky) dog 会查看左边没有lucky的dog 。

扩展阅读: