正则表达式: 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)就是给自己看的罗.