前言
这是一份比较简单的正则表达式指南,是我按照自己学习正则时做的笔记整理出来的,并不能保证没有瑕疵,但我会尽最大可能来完善它,特此我在标题上加上了
持续更新。
格式
为了使大家更加容易阅读,我在使用了几处高亮, 例子统一用黄色标注,
注意事项使用红色警示,
专业术语加粗。
目录
- 引言
- 什么事正则表达式
- 元字符
- 重复
- 转义字符
- 反义
- 字符类
- 分支条件
- 分组
- 向后引用
- 零宽断言
- 负向零宽断言
- 注释
- 贪婪与懒惰
- 附录
引言
最近给大一讲解了下正则表达式,顺便整理了份资料,共享出来。可能有点小偏差,但是正如题目中所说的,这是一篇不断完善,不断扩充的博文。 好了,言归正传,我们平时会遇到比较多的字符串,尤其是在处理文本的时候,我想这方面做Linux运维的应该深有体会。如果我们需要搜索某个文本中
具有某种规律的字符串时应该怎么办呢?很明显grep+正则。下面我就来简单介绍下什么是正则表达式,以及它的用法。
什么是正则表达式?
在计算机科学中,是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。如,我们可以使用 25[0-5]|2[0-4]\d|^1?[1-9]?\d$来匹配0-255。
元字符
\b | 匹配字符串的开始或结束(隔离空格) |
\w | 匹配字母、数字、下划线或汉字 |
\s | 匹配任意的空白符 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
. | 匹配除换行符以外的任意字符 |
\0nn | 匹配ASCII中八进制代码为nn的字符 |
\xnn | 匹配ASCII中十六进制代码为nn的字符 |
\unnnn | 匹配Unicode中十六进制代码为nn的字符 |
\cN | 匹配ASCII控制符 |
这些都是十分基础的元字符,下面我们给出2个例子:
- ^\d\s\d$ 这条正则表达式表示一个字符串,它的特征是以数字开始和结束,并且两个数字之间有个空格
- \btt\b 这条正则表达式能够识别“cattac tt”中的后一个“tt”
重复
* | 重复零次或更多次 |
+ | 重复一次或多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多 |
{n,m} | 重复n到m次 |
重复如果使用得当,能够极大地缩短正则表达式地长度,以下是2个简单的例子:
- Linux\d+ 这条表达式能够匹配Linux以及其后更随的至少一个数字,有点拗口,打个比方,Linux3
- \b\d{7}\b 这条表达式将能够匹配独立的7个数字组成的字符串
转义字符
从上面的元字符中我们可以看到有些字符被正则表达式给占用了,此时我们就需要将他们转义,十分简单,我们只需要在被占用的字符前面加上\,对就像大多数程序语言一样处理就行了,如\.,\^,\$,\\,\(,\)等等。
\a | 报警 |
\t | 制表符 |
\v | 竖向制表符 |
\f | 换页符 |
\n | 换行符 |
\e | Esc |
反义
\W | 匹配任意不是\w的字符 |
\S | 匹配任意不是\s的字符 |
\D | 匹配任意不是\d的字符 |
\B | 匹配任意不是\b的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^as] | 匹配除了as以外的任意字符 |
反义就相当于元字符的补集,其中的\B比较难以理解。 那么,我们来深究下\b到底表示的是什么东西?它表示一个特殊位置,即字符与空格之间的位置。 由此类比,\B表示的是字符与字符之间的位置,不过这里的字符并不包括空白字符。 \B并不常用,它包含的位置实在是太具普遍性了。。。 同样,我们给出2个例子:
- \b[^z]+\b 匹配不含z的字符串
- <s[^>\s]+> 匹配用<>括起来的以s开头的不包含空格的字符串
字符类
上述的\d表示数字0-9,那么假设我们只需要匹配2-5,那么该怎么办?显然,到目前为止,我们并没有提到相关技术。 此时我们需要的是自己定义字符类了,方法很简单,使用[]将你需要定义的字符类括起来就行了,以下是几个例子:
- [2-5] 表示匹配2-5,当然你完全可以使用[2345],如果你不嫌累的话
- [a-z] 匹配所有小写字母
- [a-zA-Z] 匹配所有大小写字母
分支条件
什么是分支条件呢?请不要着急,我们先来讲述一个案例。 假设现在我们有2串电话号码,分别是(010)12345678和010-12345678,它们均是某电话号码的合法表示,那么我们应该如何来通过正则表达式来匹配呢? 很简单,使用分支——“|”,也可以理解为“或”。接下来我们就试着用分支来解决上述问题。
- \(010\)\d{8}匹配(010)那种表示方式
- 010-\d{8}匹配另外一种
- 我们将上述两个式子通过|连接,即(\(010\)|010-)\d{8}
分组
细心的读者应该会发现,到目前为止我们所针对都是单个字符的匹配,那么一段字符串如何精确匹配? 这个时候我们就要引入“分组”的概念了。所谓分组,其实就是使用()将需要匹配的字符串括起来,这个时候的重复就是针对整个括号内的内容了。同时,分组也为我们引入新的概念奠定了基础,这个后面会提到。 用例子来说话:
- ([a-z]\d)+ 匹配一个或者多个以小写字母和数字的组合
- (\d{3}.){3}\d{3} 实现一字符串,其具体形式类似于ip,只是其有效值为000-999,我们暂时不考虑复杂的例子
向后引用
其实分组还有个特性,那就是它的识别字符会被引擎直接标号,供我们接下来直接引用,下面我们就来介绍这方面的特性。 首先是格式,这里的具体格式有两种,其一是使用默认组号;其二是自定义标记。下面我们分开来介绍。
默认形式:\b(\w+)\b\s+\b\1\b 我将相关部分使用红色标识了,这里的第一个分组内容被引擎匹配后,会被编上号码(注意,是从1开始的),接下来我们就可以通过\1来引用了 用户定义: \b(
?<Word>\w+)\b\s+\b
\k<Word>\b 这个表达式的功能和上面是一样的只是我们此时将默认标识换成了自己定义的标志,不过这里其实还有另一种形式 \b(
?'Word'\w+)\b\s+\b
\k'Word'\b
零宽断言
零宽断言,听起来很玄乎,其实它和之前介绍的^,$,\b一样,都是用来标识一个位置,只不过我们可以自由定义这个位置罢了。 (?=
exp)也叫
零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式
exp。比如 \b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。 (?<=
exp)也叫
零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式
exp。比如 (?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。 下面这个例子同时使用了这两种断言:
- (?<=\s)\d+(?=\s)匹配以空白符间隔的数字(不包括这些空白符)。
负向零宽断言
零宽断言是定义符合条件的位置,那么必然,它是有补集的,那就是负向零宽断言。多位负向零宽断言,就是指不符合定义的位置。
(?!exp)也叫 零宽度负预测先行断言,断言此位置的后面不能匹配表达式
exp。例如: \d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字; \b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。
(?<!exp)也叫 零宽度负回顾后发断言,断言此位置的前面不能匹配表达式
exp。例如: (?<![a-z])\d{7}匹配前面不是小写字母的七位数字。
注释
就像程序语言能够添加注释一样,正则表达式也是可以的,尤其是在式子比较长的时候,注释显得尤为重要。使用注释的方式就是(?#comment),例如 2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。
贪婪与懒惰
正则表达式是具有双重人格的,为什么这么说呢?且看本段。 当正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符。例如: a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这就是
贪婪匹配。 有时,我们更需要
懒惰匹配——匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。例如:
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab和ab。 *? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
附录
修改记录:
2013-3-8 --> 编写正则表达式最基础的知识点
如要转载本人的文章,请注明出处。