正则表达式的三种模式【贪婪、勉强、侵占】
假定要分析的字符串是xfooxxxxxxfoo
模式.*foo (贪婪模式): 模式分为子模式p1(.*)和子模式p2(foo)两个部分. 其中p1中的量词匹配方式使用默认方式(贪婪型)。 匹配开始时,吃入所有字符xfooxxxxxx去匹配子模式p1。匹配成功,但这样以来就没有了字符串去匹配子模式p2。本轮匹配失败;第二轮:减少p1部分的匹配量,吐出最后一个字符, 把字符串分割成xfooxxxxxxfo和o两个子字符串s1和s2。 s1匹配p1, 但s2不匹配p2。本轮匹配失败;第三轮,再次减少p1部分匹配量,吐出两个字符, 字符串被分割成xfooxxxxxxfo和oo两部分。结果同上。第四轮,再次减少p1匹配量, 字符串分割成xfooxxxxxx和foo两个部分, 这次s1/s2分别和p1/p2匹配。停止尝试,返回匹配成功。
模式.*?foo (勉强模式): 最小匹配方式。第一次尝试匹配, p1由于是0或任意次,因此被忽略,用字符串去匹配p2,失败;第二次,读入第一个字符x, 尝试和p1匹配, 匹配成功; 字符串剩余部分fooxxxxxxfoo中前三个字符和p2也是匹配的. 因此, 停止尝试, 返回匹配成功。在这种模式下,如果对剩余字符串继续去寻找和模式相匹配的子字符串,还会找到字符串末尾的另一个xfoo,而在贪婪模式下,由于第一次匹配成功的子串就已经是所有字符,因此不存在第二个匹配子串。
模式.*+foo (侵占模式): 也叫占用模式。匹配开始时读入所有字符串, 和p1匹配成功, 但没有剩余字符串去和p2匹配。因此, 匹配失败。返回。
简单地说, 贪婪模式和占有模式相比, 贪婪模式会在只有部分匹配成功的条件下, 依次从多到少减少匹配成功部分模式的匹配数量, 将字符留给模式其他部分去匹配; 而占用模式则是占有所有能匹配成功部分, 绝不留给其他部分使用。
再看下面一个例子:贪婪模式与侵占模式的比较
正则:\w+[a-z]与\w++[a-z]
目标串:232hjdhfd7474$
分析:①\w+[a-z]:\w+属于贪婪模式,会一次性吃掉它所能吃掉的所有的字符,也就是子串232hjdhfd7474,此时[a-z]不能够找到匹配了,故\w+匹配的串会吐出一个字符4,但此时还是得不到匹配。反复的这样吐出回退,直到吐出字符d时,此时[a-z]能够匹配h,所以这时正则表达式会返回一次成功的匹配结果,为232hjdhfd
②\w++[a-z]:\w++属于侵占模式,它会一次性吃掉它所能够吃掉的所有字符,即子串232hjdhfd7474,而且不留给其他部分使用,故不会回退。此时[a-z]不能够找到匹配,所以此次匹配失败。在余下的子串中也找不到能匹配成功的子串。所以整个正则表达式是找不到匹配结果的!
量 词 种 类 | 意 义 | ||
贪婪 | 勉强 | 侵占 | |
X? |
X?? |
X?+ |
匹配 X 零次或一次 |
X* |
X*? |
X*+ |
匹配 X 零次或多次 |
X+ |
X+? |
X++ |
匹配 X 一次或多次 |
X{n} |
X{n}? |
X{n}+ |
匹配 X n 次(这个应该不存在这几种模式,就是固定匹配n个) |
X{n,} |
X{n,}? |
X{n,}+ |
匹配 X 至少 n 次 |
X{n,m} |
X{n,m}? |
X{n,m}+ |
匹配 X 至少 n 次,但不多于 m 次 |
正则表达式高级用法(分组与捕获)
分组的引入:
对于要重复单个字符,非常简单,直接在字符后卖弄加上限定符即可,例如 a+ 表示匹配1个或一个以上的a,a?表示匹配0个或1个a。这些限定符如下所示:
X ? |
X ,一次或一次也没有 |
X * |
X ,零次或多次 |
X + |
X ,一次或多次 |
X { n } |
X ,恰好 n 次 |
X { n ,} |
X ,至少 n 次 |
X { n , m } |
X ,至少 n 次,但是不超过 m 次 |
但是我们如果要对多个字符进行重复怎么办呢?此时我们就要用到分组,我们可以使用小括号"()"来指定要重复的子表达式,然后对这个子表达式进行重复,例如:(abc)? 表示0个或1个abc 这里一 个括号的表达式就表示一个分组 。
分组可以分为两种形式,捕获组和非捕获组
分组就是对文本加括号以帮助执行某种操作,比如:
- 在两种或更多可选模式中选择一个
- 创建子模式
- 捕获一个分组后以便之后向后引用
- 对组合的模式使用某项操作(如量词)
- 使用非捕获分组
- 原子分组
选择操作
选择操作可在多个可选模式中匹配一个。例如,你想在"The rime of the Ancyent Mariner"中找出the出现过多少次,包括THE,The和the的形式。
若在RegExr上方文本框输入
(THE|The|the)
则看到所有the都被标亮。
可以使用选项来使分组更简短。例如:
(?i)
可以让模式不再区分大小写。所有上面带选择操作的模式可以写成
(?i)the
正则表达式中的选项
子模式
正则表达式中的子模式是指分组中的一个或多个分组。
例如:
(the|The|THE) (t|T)h(e|eir)
括号对于子模式不是必须的。
\b[tT]h[ceinry]*\b
这个模式会匹配the或The还有thee,thy以及thence等单词。
- \b匹配单词起始边界
- [tT]是字符组,它匹配小写字母t或者大写字母T。可以看做是第一个子模式。
- 然后匹配小写字母h
- 第二个也就是最后一个子模式也表示为字符组[ceinry],后面量词*表示一个或多个
- \b单词的结尾边界
捕获分组和后向引用
当一个模式的全部或者部分内容由一对括号分组时,它就对内容进行捕获并临时存储与内存中。可以通过后向引用重用捕获的内容。
\1
或者
$1
这里\1或$1引用的是第一个捕获的分组,而\2或$2引用的是第二个捕获的分组,以此类推。
命名分组
命名分组就是有名字的分组。
假如你要查找含有连续六个0的字符串:
000000
就可以用这个模式对连续三个0的分组命名:
(?<z>0{3})
然后你可以再使用该分组:
(?<z>0{3})\k<z>
或者
(?<z>0{3})\k'z'
或者
(?<z>0{3})\g{z}
命名分组的语法
非捕获分组
非捕获分组不会将内容存储在内存中。在你并不想引用分组的时候可以使用,因为没有存储内容,所以可以带来性能上的提升。
(the|The|THE)
这个分组不需要任何后向引用,所以可以写成非捕获分组:
(?:the|The|THE)
添加选项将其变为不区分大小写的模式:
(?i)(?:the)
也可以这样写:
(?:(?i)the)
最推荐的写法是这样的:
(?i:the)
原子分组
另一种非捕获分组是原子分组。
如果你使用的正则表达式引擎进行回溯操作,这种分组就可以将回溯操作关闭,但它只针对原子分组内的部分,而不针对整个正则表达式。
(?>the)
小结:
1.(THE|The|the),通过竖线|可以多个可选模式中匹配一个
2.括号()内的模式和字符组[]都可以看做一个分组
3.括号()内的分组会被捕获到内存中,使用\1或者$1后向引用
4.通过命名的分组可以用名字来后向引用
5.(?:the|The|THE)非捕获分组不会存在内存中,以提高性能
匹配unicode字符
有时候我们需要匹配ASCII范围之外的字符。
"Qu’est-ce que la tolérance? c’est l’apanage de l’humanité. Nous sommes tous pétris de faiblesses et d’erreurs; pardonnons-nous réciproquement nos sottises, c’est la première loi de la nature." —Voltaire (1694–1778) What is tolerance? It is the consequence of humanity. We are all formed of frailty and error;let us pardon reciprocally each other's folly--that is the first law of nature.
我们将伏尔泰的名言输入到http://www.regexpal.com/中,然后输入正则表达式
\u00e9
\u之后跟着的十六进制值00e9,这里不区分大小写,00E9也可以,00E9对接十进制值233,在ASCII(0~127)之外。
注意在Regexpal中字母é,即小写e加上了一个重音符,被标亮了,这是因为在unicode中é就是U+00E9,所以\u00e9可以匹配到它。
Regexpal.com是javascript的正则表达式实现。javascript也允许使用以下语法实现:
\xe9
现在我们在其他正则引擎中试一下。http://regexhero.net/tester/是.NET编写的。
古池 蛙飛び込む 水の音 —芭蕉 (1644–1694) At the ancient pond a frog plunges into the sound of water. —Basho (1644–1694)
以上是日本诗人松尾芭蕉的俳句。
将其输入regexhero,然后输入正则
\u6c60
这是单词pond池塘所对应的日文字符的代码点。
另外,也可以匹配一下长破折号
\u2014
或短破折号
\u2013
用八进制数匹配字符
在正则中,用八进制数就是在反斜线后加三位数字。
比如
\351
等同于
\u00e9
匹配控制字符
代码库的ASCII.txt里是所有ASCII字符,一个字符一行,一共128行。
在正则表达式中,像这样来指定一个控制字符:
\cx
其中x就是你想匹配的控制字符
匹配unicode和其他字符
正则表达式入门(七)量词
1 22 333 4444 55555 666666 7777777 88888888 999999999 0000000000
将排列成直角三角形的数字粘贴到程序中。
贪心,懒惰和占有
量词本身是贪心的,贪心的量词会首先匹配整个字符串。尝试匹配时,它会选定尽可能多的内容,也就是整个输入。量词首次尝试匹配整个字符串,如果失败则回退一个字符后再尝试,这个过程叫做回溯。它每次回退一个字符,知道找到匹配内容或者没有字符尝试为止。
懒惰的意思就是它从目标的起始位置开始尝试寻找匹配,每次检查字符串的一个字符。最后它会尝试匹配整个字符串。要使一个量词成为懒惰的,必须在普通量词后添加一个问好?
占有量词会覆盖整个目标然后尝试寻找匹配内容,但它只尝试一次,不会回溯。占有量词就是在普通量词之后添加一个加号。
用*,+和?进行匹配
在工具中输入
.*
它会以贪心的方式匹配文本中的所有字符。*匹配任何字符零次或多次。
贪心的量词
匹配特定次数
使用花括号可以限制某个模式在某个范围内匹配的次数,未修饰的量词是贪心量词。
例如:
7{1}
会匹配第一次出现的7,要匹配一个或多个7,只需要加一个逗号:
7{1,}
所以,7+其实和7{1,}是一样的。
那么,7*其实和7{0,}是一样的。
最后,7?其实和7{0,1}是一样的
范围语法
懒惰量词
在工具中用?来匹配零个或一个5
5?
再加一个?使量词编程懒惰的
5??
现在它不匹配任何内容了,因为现在的模式是懒惰的。它不会强制匹配第一个5,懒惰的基本特性就是匹配尽可能少的字符。
试一下匹配零个或多个
5*?
它也不匹配任何内容,因为它选择匹配最少的次数,零次。
试一下匹配一个或多个
5+?
它匹配了一个5,它选择了最少的次数,一次。
使用m和n的方式也是一样的
5{2,5}?
它将匹配2个5,而不是5个。
如果你想匹配最少而不是最多的数目,就使用懒惰量词。
占有量词
占有式量词很像贪心式量词,它会选择尽可能多的内容,但它不回溯。它不会放弃找到的内容,它很自私。优点是速度快。
我们先匹配以零开头的多个零,然后再匹配以零结尾的多个零。
0.*+
所有零都被标亮了。
.*+0
不匹配任何内容。因为它没有回溯,一下子选定了所有的输入,不再回来查看。它一下子没有在结尾找到零,也不知该从哪里找起。
环视是一种非捕获分组,它根据某个模式之前或之后的内容匹配其他模式。环视也称为零宽度断言。
环视包括:
- 正前瞻
- 反前瞻
- 正后顾
- 反后顾
THE RIME OF THE ANCYENT MARINERE, IN SEVEN PARTS. ARGUMENT. How a Ship having passed the Line was driven by Storms to the cold Country towards the South Pole; and how from thence she made her course to the tropical Latitude of the Great Pacific Ocean; and of the strange things that befell; and in what manner the Ancyent Marinere came back to his own Country. I. 1 It is an ancyent Marinere, 2 And he stoppeth one of three: 3 "By thy long grey beard and thy glittering eye 4 "Now wherefore stoppest me?
正前瞻
假设要匹配单词ancyent,且要求紧跟的单词是marinere,可以使用正前瞻。
(?i)ancyent(?=marinere)
(?i)不区分大小写,所以现在就是在每一行中寻找后跟marinere的单词ancyent。
反前瞻
反前瞻是正前瞻的取反操作。说明要在需要匹配的模式后找不到给定的前瞻内容。
(?i)ancyent(?!marinere)
这里,正前瞻的=变成了反前瞻的!。
正后顾
正后顾会查看前面的内容,和正前瞻的方向相反。
(?i)(?<=ancyent)marinere
反后顾
(?i)(?<!ancyent)marinere
标签:字符,量词,匹配,正则表达式,高级,模式,分组,字符串 From: https://www.cnblogs.com/596014054-yangdongsheng/p/10246774.html