开始真正了解RE是从这篇开始的。RE实在是博大精深,且把之前学到的东西再整理回忆下了。
正则表达式就是记录文本规则的代码。
前面那句话说的好,RE是描述的一种字符排列的规则,这就有两个要素:
1、表达形式:
表达形式包含了主体和组合方式
a) 主体:各种字符,不管一个正则表达式的字符代表了一个还是一类字符,总之,这些主体是可以看到的
b) 组合方式:也就是位置,同样的字符,位置不同,排列不同,表示的字符串也不同
所以说,表达形式这块的内容,可以直接地从RE匹配的结果中看出来。
.
|
匹配除换行符"\n"之外的任意字符 |
\b
|
代表单词的开头或结尾,即单词的分界处[ \b匹配这样的位置:它的前一个字符和后一个字符不全是\w ] RE中的单词:不少于一个的连续的\w |
\d
|
一位数字(0,1,2……9) |
\s
|
任意空白符:空格,制表符,换行符,中文全角空格 |
\w
|
字母或数字或下划线或汉字等 |
^
|
字符串的开始 |
$
|
字符串的结束 |
+
|
前面内容匹配1次或多次 |
*
|
表示数量:前边的内容可以连续重复使用任意次是整个表达式匹配,0次或多次 |
?
|
重复0次或1次 |
{n}
|
重复n次 |
{n,}
|
重复n次或多次 |
{n,m}
|
重复n次到m次 |
\
|
匹配某些特殊符号时,取消符号的特殊含义: \--\\,*--\*,.--\.,(--\(,)--\) |
[aeiou]
|
匹配一个字符,候选值为aeiou |
[0-9]
|
匹配一个数字,候选值为0,1,2,3,4,5,6,7,8,9 |
反义:大写表示与小写相反的含义
\W
|
任意不是字母、数字、下划线、汉字的字符 |
\S
|
任意非空字符 |
\D
|
任意非数字字符 |
\B
|
不是单词开头或结束的位置 |
[^x]
|
除x意外的任意字符 |
[^aeiou]
|
除aeiou以外的任意字符 |
|
2、表达方式:
在表达形式之上,如何合理、精确、简短地描述规则,就是表达方式的范畴,比如:分支、分组、后向引用、零宽断言、贪心与懒惰、递归匹配等。
可能有人会说,上一行提到的这些东西,也有是直接反映在结果中的。我相说的是,表达方式各种方法的使用,使表达形式更简洁、精确,其实有些没有也行,比如:后向引用,很显然的一个例子。
分枝条件
|
用“|”将不同的规则分隔开 eg. > 国内固定电话:0\d{2}-\d{8} | 0\d{3}-\d{7} 以“-”分割,3位区号和四位区号 > 国内固定电话,3位或4位区号,区号可以用“-”与号码隔开,也可以用()隔开,也可以用空格,也可以什么都没有: \(?0\d{2}\)?[- ]?\d{8} | \(?0\d{3}\)?[- ]?\d{7} bug: 对01234567890没法分出3位还是4位区号;对(012-34567890无匹配成功 ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)
支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号 > 美国邮政编码:\d{5}-\d{4} | \d{5} 9位,前5位后有“-”分割,或是只有5位 (注意各个条件的顺序 如果是\d{5} | \d{5}-\d{4},则值匹配5位的邮编或位的前5位,在有分枝时,只要满足一个就算匹配完成,后面会提到的Lazy原则)
|
分组
|
重复一组字符,用()来指定子表达式,即为分组 eg. > IP地址: ((2[0-4]\d | 25[0-5] | [01]?\d\d?)\.){3}(2[0-4]\d | 25[0-5] | [01]?\d\d?) |
后向引用
|
指定子表达式(分组)后,用编号来引用前面的分组,自添加的分组编号默认从1开始,\1 表示分组1匹配的文本。分组0匹配整个正则表达式。 eg. 重复的单词,如"go go" (\b(\w+)\b)\s+\1\b 可以自己给分组指定组名:(?<GroupName>expr)或(?'GroupName'expr) [ 组号匹配过程要扫描两边:1、扫描未命名的分组;2、扫描命名分组 ] eg. > IP地址: ((?<IP>2[0-4]\d | 25[0-5] | [01]?\d\d?)\.){3}\k<IP> |
零宽断言
|
“零宽”表示这个语法不在匹配串中占用任何字符 “断言”表示条件不满足即可退出 确保匹配串附近出现了某些字符 (?=exp) 零宽度正预测先行断言:断言自身出现的位置的后面能匹配表达式exp 匹配串后面是exp eg. > 一篇文章中结尾为ing的词中除ing之外的部分: \b\w+(?=ing\b?) Rolling in the deep 它匹配 Roll (?<=exp) 零宽度正回顾后发断言:断言自身出现的位置的前面能匹配表达式exp 匹配串前面是exp eg. > 匹配以re开头的词中除re之外的部分: (?<=\bre)\w+\b reading a book 它匹配ading > 以空白符间隔的数字(不包含这些空白符) (?<=\s)\d+(?=\s) |
负向零宽断言
|
确保匹配串附近没有出现某些字符 (?!exp) 零宽度负预测先行断言:断言此位置的后面不能匹配表达式exp 匹配串后面不能是exp eg. > 后面不是字母u的q的单词: \b\w*q(?!u)\w*\b (?<!exp) 零宽度负回顾后发断言:断言此位置后前面不能匹配表达式exp 匹配串前面不能是exp eg. 前面不是小写字母的七位数字: (?!<[a-z])\d{7} 不含属性的简单HTML标签里的内容: (?<=<\w+>).*(?=<\/\1>) |
注释
|
(?#comment) eg. IP地址: ((?<IP>2[0-4]\d(?#200-249) | 25[0-5](?#250-255) | [01]?\d\d?(?#0-199))\.){3}\k<IP> |
贪心
|
当RE中包含能接受重复的限定符是,通常是在能匹配的前提下,匹配尽可能多的字符。 eg. a.*b 匹配最长的以a开始,以b结束的字符串 对aabab,会匹配整个字符串 |
懒惰
|
匹配尽可能少的字符 在前面的限定符后面加?即可转化为懒惰模式 eg. a.*?b 对aabab得到的匹配是aab和ab两个
*? |
重复0次或多次,但尽可能少重复 |
+? |
重复1次或多次,但尽可能少重复 |
?? |
重复0次或1次,但尽可能少重复 |
{n,m}? |
重复n次到m次,但尽可能少重复 |
{n,}? |
重复n次以上,但尽可能少重复 |
|
最先匹配
|
最先开始的匹配具有最高的优先级 优先级高于贪心或懒惰 |
|
上面的思路或许不那么严谨,只是讲讲我自己按照怎么样的逻辑去理解这套东西的。