正则表达式 - 2018-08-07
Posted qiulinzhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正则表达式 - 2018-08-07相关的知识,希望对你有一定的参考价值。
元字符 metacharacter
- : 匹配单词的开始或结尾,只匹配一个位置
- . (点) 匹配除换行符以外的任何字符
- d 匹配数字 0-9
- s 匹配任意的空白符,包括空格、制表符、换行符、中文全角空格等
- w 匹配字母、数字、下划线
- ^ 匹配字符串的开始
- $匹配字符串的结束,比如 一个网站要求你填写的QQ号必须为5-12位数字时,可以使用^d{5,12}$,单独的{2}表示不多不少重复2次,{5,12}表示重复的次数不能少于5次,不能多余12次。
如果选中了处理多行的选项,^和$的意义为 匹配行的开始和结束处 - () 也是元字符,用来分组
- 如果要查找元字符本身,比如 . 和 * ,就需要使用反斜杠 来进行转义,查找反斜杠 ,也得用
限定符 指定数量的代码
代码 | 说明 |
---|---|
* | 重复 0 次或者重复多次 |
+ | 重复 1 次或者重复多次 |
? | 重复 0 次或者 1 次 |
{n} | 重复了 n 次 |
{ n, } | 重复了 n 次或者更多次 |
{m,n} | 重复 m 到 n次 |
字符类
使用中括号 []
[aeiou]
匹配任何一个元音字母[.?!]
匹配标点符号 . ? ![0-9]
匹配数字 0-9 与d
代表的意义相同[0-9a-zA-Z]
与w
等价(?0d{2}[(-]?d{8}
首先是一个转义字符(
它出现0次或1次(?
), 然后是数字 0,后面跟着 2 个数字 (d{2}
), 然后是空格或者-
中的一个,它出现1次或者不出现(?
),最后是8个数字(d{8}
) 。上式可以匹配010)88886666,或022-22334455,或02912345678等常见电话号码格式,但是也能匹配 010)12345678或(022-87654321这样的“不正确”的格式,因此就要使用下面的 分支条件分支条件
用
|
号把不同的规则分隔开
e.g.(0d{2})[- ]?d{8}|0d{2}[- ]?d{8}
这个表达式匹配三位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号支架你可以用连字号或空格间隔,也可以不用。
注意: 匹配分支条件时,将会从左到右地测试每个条件,如果满足了某个条件将不会再去管其他的条件
比如:d{5}-d{4}|d{5}
用于匹配美国的邮政编码,美国邮编的规则是5位数字或者用连字号间隔的9位数字;如果写成:
d{5}|d{5}-d{4}
那么就只会匹配5位的邮编,以及9位邮编的前五位
分组
e.g. 描述一个IP地址
((2[0-4]d|25[0-5]|[01]?dd?).){3}(2[0-4]d|25[0-5]|[01]?dd?)
要点:2[0-4]d|25[0-5]|[01]?dd?
分为三种情况:
- 一个2,然后0-4中选一个,最后第三位任选(200-249)
- 一个25,然后0-5中选一个 (250-255)
- 0和1中选一个,可以不选,再是一个数字,最后第三位可有可没有(0-199)
最后再是一个.
表示IP四个数字之间的分隔,由于最后面一个数字不带点,所以要分开,先写前面3个 - 整个最外层的括号就代表分组,
{3}
代表前面的部分重复三次
反义
采用大写的方式,与原来的小写方式表达相反的含义
|代码|说明|
|:-:|:-:|
|W
| 与w
相对应,匹配不是字母、数字、下划线的字符
|S
|与s
相对应,匹配任何不是空白符的字符
|D
|与d
相对应,匹配任意非数字的字符
|B
|与相对应,匹配不是单词开头或结束的位置
|[^x]
|匹配除x
以外的字符
|[^aeiou]
|匹配除aeiou以外的任意字符
后向引用
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本,1
代表分组1匹配的文本
e.g.
(w+)s+1可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(
(w+)
),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(s+
),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(1
)。也可以自己指定表达式的组名,e.g.指定一个表达式的组名为
Word
,则可以使用(?<Word>w+)
,其中尖括号也可以用引号‘
来替代,即(?‘Word‘w+)
,当反向需要引用这个分组捕获的内容时,可以用k<Word>
代码 | 说明 |
---|---|
(exp) |
匹配 exp ,并捕获文本到自动命名的组里 |
(?<name>exp) |
匹配 exp ,并捕获文本到名称为 name 组里,也可以(?‘name‘exp) |
?:exp |
匹配 exp ,不捕获匹配的文本,也不给此分组分配组号 |
零宽断言
(?=exp)
它断言自身出现的位置后面可以匹配表达式 exp ,比如:w+(?=ing)
匹配以 ing 结尾的单词的前面部分(除了 ing 以外的部分),比如在查找 I am singing while you are dancing. 它会匹配 sing 和 danc,称之为:零宽度正预测先行断言(?<=exp)
它断言自身出现的位置前面可以匹配表达式 exp ,比如(?<=re)w+
,会匹配以 re 开头的单词的后半部分(除 re 以外的部分),比如在查找 reading a book ,会匹配 ading。称之为零宽度正回顾后发断言
e.g. (?<=s)d+(?=s)
匹配以空白字符间隔的数字(不包括这些空白字符)
负向零宽断言
(?!exp)
断言此位置的后面不能匹配表达式 exp
e.g.
d{3}(?!d)
匹配三位数字,而且这三位数字的后面不能是数字
((?!abc)w)+
匹配不包含连续字符串 abc 的单词
一个更复杂的例子:(?<=<(w+)>).*(?=</1>)
匹配不包含属性的简单html标签内的内容;(?<=<(w+)>)
指定了这样的前缀:被尖括号括起来的单词;然后是.*
,即任意字符串,最后是一个后缀?=</1>
,这里/
(斜杠和反斜杠)b用到了前面的字符转义;1
用到了反向引用,引用的正是捕获的第一组w+
匹配的内容,这样,如果前缀是, 那后缀就是 ;整个表达式匹配的是和之间的内容(不包括前缀和后缀)
注释
- 小括号的一种用途是通过
?#comment
来包含注释,例如:
2[0-4]d(?#200-249)|25[0-5](?# 250-255)|[01]?dd?(?#0-199)
- 要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:
(?<= #断言要匹配的文本的前缀
<(w+)> #查找尖括号括起来的字母或数字(即HTML或XML标签)
) #前缀结束
.* #匹配任意文本
(?= #断言要匹配的文本的后缀
</1> #查找尖括号括起来的内容
) #后缀结束
贪婪与懒惰
- 表达式
a.*b
将匹配 最长的以 a 开始,以 b 结束的字符串,如果用其来搜索 aabab 它会匹配整个字符串;这被称之为 贪婪匹配,匹配 - 有时我们需要匹配尽可能少的字符,因此可在后面加一个限定符
?
,即.*?
表示匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复;
比如:a.*?b
匹配最短的,以 a 开始,以 b 结束的字符串;如果将其应用于 aabab,它会匹配到第一个aad(第1个字符到第3个字符)和 ab (第4-5个字符)- 为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。
代码 | 说明 |
---|---|
*? |
重复任意次,但是尽可能少的重复 |
+? |
重复1次或更多次,但尽可能少的重复 |
?? |
重复0次或1次,但尽可能少重复 |
{m,n}? |
重复 m-n 次,但是尽可能少重复 |
{n,}? |
重复n次以上,但是尽可能少重复 |
平衡组/递归匹配
有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用(.+)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?
为了避免(和(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa
(?‘group‘)
把捕获的内容命名为group,并压入堆栈(stack)(?‘-group‘)
从堆栈上弹出最后压入堆栈的名为 group 的捕获内容,如果堆栈为空,则本组的匹配失败(?(group)yes|no)
如果堆栈上存在以名为group的不捕获内容的话,继续匹配 yes 部分的表达式,否则匹配 no 部分(?!)
零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败
思路:每碰到左括号,就压入一个 "Open" ,每碰到一个右括号,就弹出一个,到了最后看堆栈是否为空,如果不为空则证明左括号比右括号多,那匹配就应该失败。正则表达式会进行回溯(放弃最前面或最后面的一些字符),尽量使整个班表达式得到匹配
< #最外层的左括号
[^<>]* #最外层的左括号后面的不是括号的内容
(
(
(?‘Open‘<) #匹配到了左括号,往堆栈压入一个‘Open‘
[^<>]* #匹配左括号后面不是括号的内容
)+ #可以出现多次
(
(?‘-Open‘>) #碰到了右括号,则从堆栈弹出一个‘Open‘
(^<>)* #匹配右括号后面不是括号的内容
)+ #可以出现多次
)* #整个上面可以出现多次
(?(Open)(?!)) #在遇到最外层的右括号前面,判断堆栈上是否还有"Open",
#如果还有,则执行 (?!),由于后面没有后缀表达式,匹配总是失败
> #最外层的括号
以上是关于正则表达式 - 2018-08-07的主要内容,如果未能解决你的问题,请参考以下文章
正则表达式匹配特定的 URL 片段而不是所有其他 URL 可能性