正则表达式必知必会读书笔记

阅读: 评论:0

正则表达式必知必会读书笔记

正则表达式必知必会读书笔记

文章目录

  • 1.概述
  • 2.匹配单个字符
  • 3.匹配一组字符
  • 4.使用元字符
    • 4.1空白元字符
    • 4.2匹配特定的字符类别(类元字符)
    • 4.3匹配字母和数字
    • 4.4匹配空白字符
    • 4.5匹配十六进制或八进制数值
    • 4.6使用POSIX字符类
  • 5. 重复匹配
    • 元字符表
    • 例1:`+`的用法,匹配一个或多个字符
    • 例2:`+`的用法,匹配一个或多个字符集合
    • 例3:`*`的用法
    • 例4. `?`的方法
    • 例5: `{n}`的用法,匹配6进制颜色
    • 例6:`{n,m}`的用法,检查日期
    • 例7:`{n,}`的用法,出来所有大于或等于$100美元的金额
    • 例8:`?*`的用法,匹配原始文本的两个<B>标签里的文本
  • 6.位置匹配
    • 元字符表
  • 7.使用子表达式
    • 元字符表
    • 定义
    • 例子
  • 8.回溯引用:前后一致匹配
    • 8.1 定义
    • 8.2 回溯引用的例子
    • 8.3 回溯引用在替换操作中的应用
    • 8.3.1大小写转换
  • 9.前后查找
    • 元字符表
    • 9.1 向前查找的例子
    • 9.2 向后查找的例子
    • 9.3 把向前查找和向后查找结合起来
    • 9.4 对前后查找取非
  • 10. 嵌入条件
    • 10.1 回溯引用条件
    • 10.2 前后查找条件

1.概述

这篇文章是我看完正则《表达式必知必会》这本书之后做的笔记,结合后面看的《精通正则表达式》这本书,修正了对《必知必会》这本书中理解不清晰甚至是书里描述不准确的地方,这也让我明白了一个道理,有的书你看不明白或许不是你的问题,很有可能就是书里面内容的问题,同一种技术相关的书籍最好多看不同的几本,因为你在这本书不理解的地方,在另一本书中或许就豁然开朗了。

2.匹配单个字符

元字符说明
g全局匹配
i不区分大小写
.匹配任意字符(除换行符以外的任何单个字符)
用来转义特殊字符

3.匹配一组字符

元字符说明
[]字符集合,匹配该集合中的任意一个字符
-(连字符)字符区间

以下是一些合法的连字符区间:

  • A-Z,匹配从A到Z的所有大写字母。

  • a-z,匹配从a到z的所有小写字母。

  • A-F,匹配从A到F的所有大写字母。

  • A-z,匹配从ASCII字符A到ASCII字符z的所有字母。这个模式一般不常用,因为它还包含着[和^等在ASCII字符表里排列在Z和a之间的字符。

    提示 在定义一个字符区间的时候,一定要避免让这个区间的尾字符小于它的首字符(例如[3-1])。这种区间是没有意义的,而且往往会让整个模式失效。

    注意 -(连字符)是一个特殊的元字符,作为元字符它只能用在[]之间。 在字符集合以外的地方,-只是一个普通字符,只能与-本身相匹配。因此,在正则表达式里,-字符不需要被转义。

元字符说明
^取非匹配,匹配的是除了除了那个字符集里的其他字符。

^必须放在字符集中,也就是[]中才是取非的含义,如果放在一个字符集合的外面并位于一个模式的开头,^将匹配字符串的开头。 详见第六章。

注意 ^的效果将作用于给定字符集合里的所有字符或字符区间,而不是仅限于紧跟在^字符后面的那一个字符或字符区间。

4.使用元字符

元字符是一些在正则表达式里有着特殊含义的字符。因为元字符在正则表达式里有着特殊的含义,所以这些字符就无法用来代表它们本身,必须用 对元字符进行转义。

提示 任何一个元字符都可以通过给它加上一个反斜杠字符()作为前缀的办法来转义,能够被转义的元字符并不仅局限于我们这里提到的那几个。

4.1空白元字符

元字符说明
[b]回退(并删除)一个字符(Backspace键)
f换页符
n换行符
r回车符
t制表符
v垂直制表符

例子:有一组如下图所示的数据,我们需要把夹杂在这些数据里的空白行去掉

正则表达式:

rnrn

结果:

4.2匹配特定的字符类别(类元字符)

类元字符是用来代替一些常用的字符集合的特殊元字符。类元字符并不是必不可少的东西(你总是可以通过逐一列举有关字符或是通过定义一个字符区间的办法来匹配某一类字符),但用它们构造出来的正则表达式简明易懂,在实践中很有用。
下面是一些基本的类元字符的例子:
数字元字符

元字符说明
d任何一个数字字符(等价于[0-9])
D任何一个非数字字符(等价于[^0-9])

例如:myArray[d]myArray[[0-9]]的简写形式

警告 正则表达式的语法是区分字母大小写的。d匹配数字,Dd的含义刚好相反。接下来将看到的其他类元字符也是如此。

4.3匹配字母和数字

字母数字元字符

元字符说明
w任何一个字母数字字符(大小写均可)或下划线字符(等价于[a-zA-Z0-9_])
W任何一个非字母数字字符(大小写均可)或非下划线字符(等价于[^a-zA-Z0-9_])

例子:
文本

正则表达式:

wdwdwd

结果

注意 在上面这个例子里,我们使用的正则表达式解决了我们的问题。但它正确吗?请大家思考一下,为什么美国的邮政编码没有被匹配出来?是因为它们只由数字构成、还是因为什么其他原因?我们将不给出这个问题的答案,理由很简单——例子里的模式解决了问题。这里的关键是正则表达式很少有对错之分(当然,前提是它们能解决问题),我们更关心的是它们的复杂程度——而这要由模式匹配操作的精确程度来决定;如果你需要更精确的匹配,就需要构造更复杂的正则表达式。

4.4匹配空白字符

空白字符元字符

元字符说明
s任何一个空白字符(等价于[fnrtv])
S任何一个非空白字符(等价于[^fnrtv])

注意 用来匹配退格字符的[b]元字符是一个特例:它不在类元字符s的覆盖范围内,当然也就没有被排除在类元字符S的覆盖范围外。

4.5匹配十六进制或八进制数值

元字符说明
x十六进制前缀,例如x0A对应于ASCII字符10(换行符),其效果等价于n
八进制前缀,数值本身是2位或3位,例如11对应于ASCII字符9(制表符),其效果等价于t

注意 有不少正则表达式实现还允许使用c前缀来指定各种控制字符。比如说,cZ将匹配Ctrl-Z。不过,在实际工作中,必须使用这种语法的情况相当少见。

4.6使用POSIX字符类

JavaScript不支持在正则表达式里使用POSIX字符类。(因此不本节不讨论)

5. 重复匹配

匹配多个连续重复出现的字符或字符集合。

元字符表

元字符说明
+匹配一个或多个字符或字符集合
*匹配0个或多个字符或字符集合
?匹配0个或一个字符或字符集
{n}为重复匹配次数设定一个精确的值,必须出现n次
{n,m}为重复匹配次数设定一个区间,最小值n和最大值m
{n,}匹配至少重复n次
*? ,+?竟可能少的匹配,*,+本身都是贪婪型,跟上?防止过度匹配

在匹配一个或多个字符集的时候,+号必须放在[]的外面,比如说,[0-9]+是正确的,[0-9+]则不是。[0-9+]表示的是匹配0-9的任意数字或+号。

提示 +是一个元字符。如果需要匹配+本身,就必须使用它的转义序列+

注意 当在字符集合里使用的时候,像.+这样的元字符将被解释为普通字符,不需要被转义——但转义了也没有坏处。[w.]的使用效果与[w.]是一样的。

提示 [ ]的常规用法是把多个字符定义为一个集合,但有不少程序员喜欢把一个字符也定义为一个集合,这么做的好处是可以增加可读性和避免产生误解。因而[r]?在功能上与r?完全等价。

提示 ?是一个元字符。如果需要匹配?本身,就必须使用它的转义序列?

注意 {}是元字符。如果需要匹配{}本身,就应该用对它们进行转义。不过,即使你没有对{}进行转义,大部分正则表达式实现也能正确地处理它们(根据具体情况把它们解释为普通字符或元字符)。话虽如此,为了避免不必要的麻烦,你最好不要依赖这种行为;在需要把{}当做普通字符来匹配的场合,还是使用它们的转义序列{}比较稳妥。

注意 重复次数可以是0。比如,{0, 3}表示重复次数可以是0、1、2或3。我们曾经讲过,?匹配它之前一个字符(或字符集合)的零次或一次出现。因此,从效果上看,?等价于{0, 1}

例1:+的用法,匹配一个或多个字符

查找电子邮件

文本

正则表达式:

w+@w+.w+

结果

例2:+的用法,匹配一个或多个字符集合

查找电子邮件

文本:

正则表达式:

[w.]+@[w.]+.w+

结果

例3:*的用法

文本

正则表达式:

w+[w.]*@[w.]+.w+

结果

例4. ?的方法

同时匹配http和https

文本

正则表达式:

https?://[w./]+

结果:

例5: {n}的用法,匹配6进制颜色

文本

正则表达式:

#[0-9A-Fa-f]{6}

结果:

例6:{n,m}的用法,检查日期

文本:

正则表达式:

d{1,2}[-/]d{1,2}[-/]d{2,4}

结果:

例7:{n,}的用法,出来所有大于或等于$100美元的金额

文本:

正则表达式:

d+:$d{3,}.d{2}

结果:

例8:?*的用法,匹配原始文本的两个标签里的文本

文本

正则表达式:

<[Bb]]>.*</[Bb]>

结果:


分析:

只找到了一个匹配而不是预期中的两个,第一个B标签和第二个B标签开头都被一起匹配返回,因为贪婪型元字符会尽可能地从一段文本的开头一直匹配到这段文本的末尾,而不是从这段文本的开头匹配到碰到第一个匹配时为止。常用的贪婪型元字符和它们的懒惰型版本:

面的例子使用*?解决了这个问题:

文本:

正则表达式:

<[Bb]>/*?</[Bb]>

结果:

6.位置匹配

位置匹配用来解决在什么地方进行字符串匹配操作的问题。

元字符表

元字符说明
b单词边界,用来匹配一个单词的开始或结尾。
B不匹配一个单词边界
^单词边界,匹配字符串的开头。
$单词边界,匹配字符串的结尾。

简单地说,b匹配的是一个这样的位置,这个位置位于一个能够用来构成单词的字符(字母、数字和下划线,也就是与w相匹配的字符)和一个不能用来构成单词的字符(也就是与W相匹配的字符)之间。

注意 b匹配且只匹配一个位置,不匹配任何字符。用bcatb匹配到的字符串的长度是3个字符(c、a、t),不是5个字符。

注意 除了用来匹配单词边界(开头或结束均可)的b,有些正则表达式实现还支持另外两个元字符:<只匹配单词的开头;>只匹配单词的结束。不过,虽然这两种元字符可以提供粒度更细的控制,但支持它们的正则表达式引擎却并不多见(据笔者所知,egrep程序是支持<>的,但许多其他文本匹配工具则不支持它们)。

^是几个有着多种用途的元字符之一。前面学过元字符^,是用来对字符集合进行“求非”操作的元字符,当它出现在一个字符集合里(被放在[]之间)并紧跟在左方括号[的后面时,它才能发挥“求非”作用。如果是在一个字符集合的外面并位于一个模式的开头,^将匹配字符串的开头。

例1: b的用法

为了让大家对位置匹配及其相关概念有一个直观的认识,我们先来看一个例子,匹配单词cat:

文本:

正则表达式:

cat

结果:

分析:

我们只想匹配单词cat,而不想把scattered中的cat也匹配出来,正确解决这个问题的办法只有一个:使用边界限定符,也就是在正则表达式里用一些特殊的元字符来表明我们想让匹配操作在什么位置(或边界)发生。

解决办法是使用下面的正则表达式:

bcatb

结果:


分析:

单词cat的前后都有一个空格,而这将与模式bcatb相匹配(空格是用来分隔单词的字符之一)。而单词scattered中的字符序列cat不能与这个模式相匹配。

这里要特别注意的是,如果你想匹配一个完整的单词,就必须在你想要匹配的文本的前后都加上b限定符。请看下面这个例子:

文本:


正则表达式:

bcap

结果:

再看下面的例子,我们只用b作为后缀,看看匹配到的结果会是什么样子:

正则表达式:

capb

结果:

例2:^的用法

下面这个简单的测试可以检查一段文本是否是一篇XML文档(合法的XML文档都必须以<? xml>标签开头并有一些其他属性(比如一个版本号,如<? xml version=“1.0” ? >)。)

文本:

正则表达式:

<?xml.*?>

结果:

分析
这个模式似乎能够解决问题:<? xml匹配<? xml, .*匹配随后的任意文本(.的零次或多次重复出现), ? >匹配?>。这是一个非常不准确的测试,为什么呢,看看下面这个例子:

文本:


正则表达式:

<?xml.*?>

结果:


可以看到,匹配到的<?xml>标签并不是文档的开头,而是第二行,这并不是一份合法的XML文档。
这里需要的是一个能够确保被匹配到的<? xml>标签出现在字符串最开始处的测试,而这正是^元字符大显身手的地方;如下所示:

文本:


正则表达式:

^s*<?xml.*?>

结果:


分析:

^匹配一个字符串的开头位置,所以^s*将匹配一个字符串的开头位置和随后的零个或多个空白字符(这解决了<? xml>标签前允许有空格、制表符、换行符等空白字符的问题)。作为一个整体,模式^s*<? xml.*? >不仅能正确地匹配一个位置正确的<? xml>标签,还能对合法的空白字符做出妥善处理。

提示 虽然模式^s*<? xml.*? >解决了上例中的问题,但那只是因为这个例子里的原始文本并不完整而已。如果这段原始文本是一份完整的XML文档,这个例子将变成一个“贪婪型”元字符的典型示例。还好,我们已经知道解决“贪婪型”元字符问题的最佳办法是把.*替换为.*?

除了位置上的差异,$的用法与^完全一样。比如说,在一份Web页面里,标签的后面不应该再有任何实际内容,而这一点可以用下面这个模式来检查:

正则表达式:

</[Hh][Tt][Mm][Ll]>s*$

7.使用子表达式

元字符表

元字符说明
()子表达式,对表达式进行分组和归类
(?:)非捕获型子表达式,只分组不捕获(详情请看下面介绍)

定义

子表达式的作用:对表达式进行分组和归类,把那些子表达式当作一个独立元素来使用。

提示()是元字符。如果需要匹配()本身,就必须使用它的转义序列()

(?:)的用法

我们知道,子表达式的顺序按照它们的左括号的位置依次排序,例如^([-+]?[0-9]+(.[0-9]*)?)([CF])$,在引用中,1代表([-+]?[0-9]+(.[0-9]*)?),2代表的是子表达式(.[0-9]*),3代表([CF]),那么如果我们要忽略(.[0-9]*)在位置中的排序,让2代表之前的3位置的正则([CF]),应该怎么做呢,这时只需要在(.[0-9]*)左括号后面加上?:让它变成(?:.[0-9]*),下面是测试结果:

例子

例1 用正则把&nbsp;连续两次或更多次的重复出现找出来:

文本:

错误的正则表达式:

&nbsp;{2,}

结果:

分析:

结果并不是我们预期的结果,因为{2, }只作用于紧挨着它的前一个字符——那是一个分号。如此一来,这个模式只能匹配像&nbsp; ; ; ;;这样的文本,但无法匹配&nbsp; &nbsp;

运用子表达式的正则:

(nbsp;){2,}

结果:


分析:

(&nbsp;)是一个子表达式,它将被视为一个独立元素,而紧跟在它后面的{2, }将作用于这个子表达式(不仅仅是分号)。这个模式解决了我们的问题。

例2 用一个正则表达式来查找IP地址。IP地址的格式如12.159.46.200。

文本:

普通正则表达式:

d{1,3}.d{1,3}.d{1,3}.d{1,3}

d{1, 3}在这个模式里重复了4次,它同样可以被表达为一个重复,我们可以用子表达式的写法让这个正则更加的简洁。

运用子表达式的正则:

(d{1,3}.){3}d{1,3}

结果:

提示 为了提高可读性,有不少用户喜欢给表达式的每一个子表达式都加上括号。比如,把上面那个例子里的模式写成(d{1, 3} .){3}(d{1, 3})。这种做法在语法上完全成立,对表达式的实际行为也没有任何不良影响(但视乎具体的正则表达式实现,这对匹配操作的速度可能会有点儿影响)。

例3 把一条用户记录里的年份数字完整地匹配出来。

文本:

正则表达式:

19|20d{2}

结果:

分析:

为了排除没有实际意义的结果,我们把前两位数字限定为19和20。但该例子只匹配到了19,并没有把年份的四位数字完整的匹配出来。因为|字符是正则表达式语言里的或操作符,19|20将匹配数字序列19或20。但因为|操作符会把位于它左边和右边的两个部分都作为一个整体来看待。在这个例子中19|20d{2}被解释为19或20d{2}(也就是把d{2}解释为以20开头的那个表达式的一部分)。换句话说,它将匹配数字序列19或以20开头的任意4位数字。最终的结果你们已经看到了,它只匹配到了19。

这个例子的正确答案是把19|20归为一个子表达式,如下所示:

正则表达式:

(19}20)d{2}

结果:

例4 查找ip地址,IP地址里的每一组数字都不能大于255,基本思路:

  • 任何一个1位或2位数字。
  • 任何一个以1开头的3位数字。
  • 任何一个以2开头、第2位数字在0~4之间的3位数字。
  • 任何一个以25开头、第3位数字在0~5之间的3位数字。

文本:

正则表达式:

(((d{1,2})|(1d{2})|(2[0-4]d)|(25[0-5])).){3}((d{1,2})|(1d{2})|(2[0-4]d)|(25[0-5]))

结果:

提示 上面这个例子里的正则表达式看起来很难理解。把它们弄明白的关键是要把它们分解开、每次只分析和理解一个子表达式。在分析各个子表达式的时候,应该按照先内后外的原则来进行而不是从第一个字符开始一个字符一个字符地去尝试。你有过几次这样的经验之后就会发现,嵌套子表达式并不像它们看上去那么复杂。

8.回溯引用:前后一致匹配

8.1 定义

本章将讨论子表达式的另一个重要用途——定义回溯引用(backreference)。

回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式。

例子,1代表着模式里的第1个子表达式,2代表着第2个子表达式、3代表着第3个;依次类推。

提示 你们可以把回溯引用想像成变量。

警告 回溯引用只能用来引用模式里的子表达式(用()括起来的正则表达式片段)。

注意 子表达式是通过它们的相对位置来引用的:1对应着第1个子表达式,5对应着第5个子表达式,等等。虽然受到普遍的支持,但这种语法存在着一个严重的不足:如果子表达式的相对位置发生了变化,整个模式也许就不能再完成原来的工作,删除或添加子表达式的后果可能更为严重。为了弥补这一不足,一些比较新的正则表达式实现还支持“命名捕获”(named capture):给某个子表达式起一个唯一的名字,然后用这个名字(而不是相对位置)来引用这个子表达式。因为命名捕获还没有得到广泛支持,而且已支持的实现具体的语法也极不统一,所以本书没有对此进行讨论。但是,如果你正在使用的正则表达式实现支持命名捕获功能(如.NET),你应该充分利用。

8.2 回溯引用的例子

例1:用正则将html标签<H1><H6>的标签及文字全都查找出来,例子如下:

文本:

这是一个存在问题的正则表达式:

<Hh[1-6]>.*?</Hh[1-6]>

结果:

上面的正则匹配到了一个<h2>开头<h3>结尾的错误标签,解决方法如下

使用回溯引用的正则表达式:

<[hH]([1-6])>.*?<[hH]1>

结果:


分析:

们这次用()[1-6]括了起来,使它成为了一个子表达式。在结束标签的</[hH]1>1来引用这个子表达式,子表达式([1-6])匹配数字1~6, 1只匹配与之相同的数字。

例2 将文本中重复出现的单词查找出来:

文本:

正则表达式:

[]+(w+)[]+1

结果:


分析:

[ ]+匹配一个或多个空格,w+匹配一个或多个字母数字字符。与1是一个回溯引用,它引用的是前面用圆括号括起来的子表达式(w+):当(w+)匹配到单词of的时候,1也匹配单词of;当(w+)匹配到单词and的时候,1也匹配单词and。

8.3 回溯引用在替换操作中的应用

一开始我没看明白,后面看了《精通正则表达式》这本书才发现替换操作中的$1,$2是perl语言中保存变量的写法,不是正则中的元字符,只是一种编程语言的写法,这些例子目的在于展示正则表达式的使用方法,而perl语言只是展示的手段。详情请参考《精通正则表达式》第二章–perl简单入门。

例1,将地址邮件替换成可点击的链接

文本:


搜索模式正则:

(w+[w .]*@[w .]+ .w+)

替换模式正则:

<A HREF="mailto:$1">$1</A>

结果:

例2,将313-555-1234这类的电话号码重新排版为(313)555-1234。
文本:

正则表达式:

(d{3})(-)(d{3})(-)(d{4})

替换:

($1) $3-$5

结果:

提示 在对文本进行重新排版的时候,把文本分解成多个子表达式的做法往往非常有用,这可以让我们对文本的排版效果做出更精确的控制。

8.3.1大小写转换

有些正则表达式实现允许我们使用指定的元字符对字母进行大小写转换。

进行大小写转换的元字符

元字符说明
E结束L或U转换
l把下一个字符转换为小写
L把L到E之间的字符全部转换成小写
u把下一个字符转换为大写
U把U到E之间的字符全部转换为大写

下面的例子,把一级标题(<H1>...</H1>)的标题文字转换为大写:
文本:

正则表达式:

(<[hH][1-6]>)(.*?)(</[hH1])

替换

$1u$2E$3

结果:

分析:
$1包含着开始标签,U$2E把第2个子表达式(标题文字)转换为大写,$3包含着结束标签。

9.前后查找

//todo:这里前后查找的描述并不准确,待修正
用正则表达式标记要匹配的文本的位置(而不仅仅是文本本身)。前后查找(lookaround,对某一位置的前、后内容进行查找)。

元字符表

元字符说明
(?=)正向前查找
(?!)负向前查找
(?<=)正向后查找
(?<!)负向后查找

注意 本章将对向前查找(lookahead)和向后查找(lookbehind)都进行讨论。常见的正则表达式实现都支持前者,但支持后者的就没那么多了。Java、.NET、PHP和Perl都支持向后查找(但有一些限制), JavaScript和ColdFusion不支持向后查找。

前后查找正则必须包含在字表达式()

9.1 向前查找的例子

文本:

正则表达式:

.+(?=:)

结果:

分析:
+匹配任意文本,子表达式(? =:)匹配:注意,被匹配到的:并没有出现在最终的匹配结果里;我们用?=向正则表达式引擎表明:只要找到:就行了,不要把它包括在最终的匹配结果里。

9.2 向后查找的例子

查找美元金额

文本:

正则表达式:

$[0-9.]+

结果:

不想让$出现在最终的匹配结果里,你该怎么办?从这个模式里简单地把$去掉能达到目的吗?
文本:

正则表达式:

[0-9.]+

结果:

这种方法匹配到了不是美元的数字4,这并不是我们想要的结果,怎么办?好办,这正是向后查找可以大显身手的地方,如下所示:

正则表达式:

(?<=$)[0-9.]+

结果:

9.3 把向前查找和向后查找结合起来

下面的例子将向前查找和向后查找可以组合在一起使用,将title标签中的文字提取出来,但不包含标签本身。
文本:

正则表达式:

(?<=<[tT][iT][lL][eE]>).*(?=</[tT][iI][tT][lL][eE]>)

结果:

分析:
(? <=<[tT][iI][tT][lL][eE]>)是一个向后查找操作,(? =</[tT][iI][tT][lL][eE]>),最终返回的匹配结果包含且仅包含标题文字.

提示 为减少歧义,在上面这个例子里,你应该对<(需要匹配的第一个字符)进行一下转义,也就是把(? <=<替换为(? <=<

9.4 对前后查找取非

前后查找还有一种不太常见的用法叫做负前后查找(negative lookaround)[插图]。负向前查找(negative lookahead)将向前查找不与给定模式相匹配的文本,负向后查找(negative lookbehind)将向后查找不与给定模式相匹配的文本。

我们通过下面的例子来演示正向后查找和负向后查找之间的区别,下面的文本既有价格又有数量,我们先来查找且只查找价格:
文本:

正则表达式:

(?<=$)d+

结果:

接下来,我们再去查找且只查找数量:
文本:

正则表达式:

b(?<!$)d+b

结果:

分析:
表达式(? <! $)是一个负向后查找,它使得最终的匹配结果只包含那些不以$开头的数值。

细心的读者可能已经注意到了,在上面这个例子里,我们还在那个负向后查找模式里用b元字符定义了两个单词边界。我们为什么要那么做呢?你看过下面这个没有使用单词边界的例子里就明白了。
文本:

正则表达式:

(?<!$)d+

结果:

分析:
请看,因为没有使用单词边界,$30里的0也出现在了最终的匹配结果里。这是因为那个0字符的前一个字符是3而不是$,它完全符合模式(? <! $)d+的匹配要求。把这个模式用b括起来从根本上解决了这个问题。

10. 嵌入条件

正则表达式语言还有一种威力强大(但不经常被用到)的功能——在表达式的内部嵌入条件处理功能。

警告 并非所有的正则表达式实现都支持条件处理。

嵌入条件的两种情况:

  • 根据一个回溯引用来进行条件处理。
  • 根据一个前后查找来进行条件处理。

10.1 回溯引用条件

例子:把一段文本里的<IMG>标签全都找出来;不仅如此,如果某个<IMG>标签是一个链接(被括在<A></A>标签之间)的话,你还要把整个链接标签匹配出来。
文本:

正则表达式:

<[Aa]s+[^>]+>s*)?<[Ii][Mm][Gg]s+[^>]+>(?(1)s*</[Aa]>)

分析:
(<[Aa]s+[^>]+>s*)?将匹配一个<A><a>标签(以及<A><a>标签的任意属性),这个标签可有可无(因为这个子表达式的最后有一个>)。接下来,<[Ii][Mm][Gg]s+[^>]+>匹配一个<IMG>(大小写均可)及其任意属性。(?(1)s*</[Aa]>)是一个回溯引用条件——?(1)的含义是:如果第一个回溯引用(具体到本例,就是<A>标签)存在,则使用s*</[Aa]>继续进行匹配(换句话说,只有当前面的<A>标签匹配成功,才继续进行后面的匹配)。如果(1)存在,s*</[Aa]>将匹配结束标签</A>之后出现的任意空白字符。

例2,通过回溯引用条件将123-456-7890和(123) 456-7890格式的号码找出来。
文本:

正则表达式:

(()?d{3}(?(1))|-)d{3}-d{4}

结果:

分析:
(?(1))|-)是一个回溯引用条件,它将根据条件是否得到满足而去匹配)-,如果(1)存在(也就是找到了一个左括号(, )必须被匹配;否则,-必须被匹配。

10.2 前后查找条件

前后查找条件只在一个向前查找或向后查找操作取得成功的情况下才允许一个表达式被使用。

例子:匹配美国的邮政编码(简称ZIP编码)。有两种格式:种是12345形式的ZIP格式,另一种是12345-6789形式的ZIP+4格式。下面是一种解决方法:
文本:

正则表达式:

d{5}(-d{4})?

结果:

匹配到了第三行不正确的格式,下面用一个前后查找条件来解决这个问题:
文本:

正则表达式:

d{5}(?(? =-)-d{4})

结果:

分析:
d{5}匹配前5位数字。接下来是一个(?(? =-)-d{4})形式的向前查找条件。这个条件使用了?=-来匹配(但不消费)一个连字符,如果条件得到满足(那个连字符存在), -d{4}将匹配那个连字符和随后的4位数字。这样一来,33333-将被排除在最终的匹配结果之外(它有一个连字符,所以满足给定条件,但那个连字符的后面没有必须出现在那里的4位数字)。

本文发布于:2024-01-29 00:22:53,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170645897611374.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23