正则表达式: regexp; regex;
正则表达式是进行字符匹配的功用, 在搜索中很常用. 例如在unix命令grep, sed, awk, find中都可以支持正则表达式, 而脚本语言使用正则表达式进行爬虫等更是常事. 正则表达式一般有这几个功能:
- 测试字符串内的模式. 测试该字符串是否合法.例如ip地址测试.
- 搜索甚至替换字符串.
- 获取指定模式字符串. 在特定区域内找到指定模式字符串, 从而进一步分析.
基本匹配
元字符和字符集
具体字符
: 明确匹配.
除了\n
外任意一个字符,要包含\n
需要(.|\n)
[a0-9]
字符集, 可用-
表范围(原-
需转义)[^abc]
字符集不包含abc.^
在字符集中为反义的意思, 反义还有如\d
反义\D
\
转义字符,对.*?+()\|
等转义^
匹配输入字符串开头,multiline属性可以使其匹配多行.$
匹配输入字符串末尾,multiline属性可以使其匹配多行.\b
匹配单词边界(就是空白和标点位置罗),如er\b
对于hiver,而非verb\B
非单词边界,如er\B
对于verb,而非hiver\d
数字[0-9]
\D
非数字[^0-9]
\w
任何单词字符,包括下划线[0-9a-zA-Z_]
\W
非单词字符,即[^0-9a-zA-Z_]
\n
,\r
,\f
,\t
,\v
匹配换行符,回车符,换页,制表,垂直制表符.\s
任何空白字符, 等价于[ \f\n\r\t\v]
\S
任何非空白字符,等价于[^ \f\n\r\t\v]
分支选择: 多种情况
abc|bcd
两者其中之一的匹配,遵循优先权法则.a(bcd|hij)z
使用括号划分分支更有效!
数量限定: 跟在字符后面代表数量限制
不加限定符
有且只有一个+
1个或多个?
不多于1个(0或1个)*
任意个(0或1或多个){n}
数量精确n个{n,}
匹配数量不少于n(>=n).{1,}
相当于+
{n,m}
匹配数量不少于n不多于m.
贪婪与懒惰: 跟在数量限定后, 最长匹配还是最短匹配
- 贪婪 : 默认情况下尽可能多的匹配,例如
a.*b
对aabab是匹配aabab. - 懒惰 : 使用
?
在匹配之后, 表示尽可能少的匹配,如a.*?b
对aabab匹配aab(1-3)和ab(4-5). 懒惰的使用如*?
,+?
,??
,{n,}?
,{n,m}?
几种情况. - 最早匹配原则 : 最先开始的匹配拥有最高优先权,此原则高于贪婪和懒惰.所以上例匹配aab而不是ab(2-3).
分组
分为捕获性分组和不捕获分组:
捕获性分组
向后引用: 使用括号, 定义作为一个组, 可以被后续引用
(exp)
: 匹配exp的内容并归入自动命名的组, 一般组从1开始.(?<Word>exp)
或(?'Word'exp)
: 将匹配内容归入名为Word的组.(\1)
以及(\k<Word>)
: 将之前匹配的组里的内容进行引用,此处引用自动命名的组1和自定义命名的组Word.
PS: 分组编号实际进行两轮: 首先对无自定义的组进行编号, 再对有别名的组进行编号, 实际有别名的组的编号均大于其余没有别名的组.
不捕获性分组
取消括号分组歧义
(?:exp)
: 非获取匹配, 即不作为分组,不储存,不分组序号. 用途在arriv(?:e|ing)
中因为要做分支不得不用()
但又避免进行分组.
零宽断言: 用于表达位置匹配,如^$/b等作用用于定位, 不会被作为匹配内容.
(?=exp)
: 断言自身出现的位置的后面能匹配表达式exp,如\b\w+(?=ing\b)
可以匹配singing的sing(?<=exp)
: 断言自身出现的位置的前面能匹配表达式exp,如(?<=\bre)\w+\b
匹配reading的ading(?!exp)
: 断言自身出现的位置的后面不能匹配表达式exp(?<!exp)
: 断言自身出现的位置的前面不能匹配表达式exp
注释
(?#comment)
注释,如(?#这匹配string)
优先权
\
> ()
,(?:)
, (?=)
, []
> *
、+
、?
、{n}
、{n,}
、{m,n}
>^
、$
、中介字符
>|
其实正则表达式比一般的搜索匹配差异是:
- 贪婪和懒惰: 最长匹配和最短匹配
- 分组和向后引用: 将之前一个匹配作为一个组, 方便后面调用.
- 表达式位置匹配(零宽断言): 这部分是使用正则式来匹配位置, 而不是作为匹配内容, 十分方便用于定位.
- 更多的元字符式样,如常用的\d, \w, \s.
- 限定数量方式更多, 比一般使用?* 更多了
{n,m}
等的使用. - 匹配分支, 一般搜索匹配支持[0-9]这种是单字符分支, 这里支持匹配式分支
###例子
(\d{1,3}\.){3}\d{1,3} # IP地址格式匹配
<a[^>]+> #匹配用尖括号括起来的以a开头的字符串。
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) #任意IP(<256)
(?<=<(\w+)>).*(?=<\/\1>) #匹配不包含属性的简单HTML标签内里的内容,注意后面实际是</组1>
再议分组
Update: 2015.12.20
分组是正则表达式一个十分重要的特色. 其实零宽断言也是一种分组, 只是不把匹配内容作为匹配输出罢了. 分组并不是说把匹配的几个放到一个组里, 而是说某种匹配作为一个组, 这个匹配可以用于后续再使用. 另外, 分组还可以通过非捕获性来实现一些特殊功能.
捕获性分组
捕获性分组就是把匹配的内容储存到指定组(自动命名或者给定命名), 以后可以用于向后引用前面某个组的内容(引用分组).
捕获性分组就是()
内的匹配, 或者给定组名的(?<name>exp)
或(?'name'exp)
. 或者引用前面内容的引用分组(\1)
或(\k<name>)
例如abacab中使用(a\w)\w*(\1)
可以捕获abacab (组为(ab,ab))而不是abac(因为使用了引用上一个分组, 所以限定了后一个分组的匹配内容)
非捕获性分组
非捕获性分组主要是为了实现某些功能, 并非为了向后引用. 一个是位置限定, 一个相当于取消括号引起的歧义.
位置限定: 零宽断言
- 零宽度正预测先行断言(positive lookahead assertion)
(?=exp)
(看后面的字符匹配, ->) - 零宽度正回顾后发断言(positive lookbehind assertion)
(?<=exp)
(看前面的字符匹配, <-) - 零宽度负预测先行断言(negative lookahead assertion)
(?!exp)
(看后面的字符不匹配, !->) - 零宽度负回顾后发断言(positive lookbehind assertion)
(?<!exp)
(看前面的字符不匹配, !<-)
取消括号歧义
(?:exp)
: 可以取消括号带来的分组歧义, 像go(?:to|ing)
, 如果是go(to|ing)
会把这里作为一个分组进行处理. 例如”goto detroid for going happy”, 前者在python的groups()返回()
, findall中返回[goto, going]
; 后者在groups()返回(goto,)
findall中返回[to, ing]
. 后者只会匹配一个组, 返回第一个匹配.
注释
(?#comment)
就是给自己看的罗.