正则表达式
正则表达式:一个字符处理标准,指令一段正则表达式字符串,用来检索、替换那个符合正则表达式的文本.
http://tool.oschina.net/regex 提供在线测试
元字符
| 元字符 | 说明 | 匹配字符 | 匹配数量 |
|---|---|---|---|
| \b | 匹配一个位置,单词的开头或结尾 | V | |
| . | 匹配除了换行符以外的一个任意字符 | V | |
| \d | 匹配一个数字 | V | |
| \s | 匹配任意的空白符,包括空格、制表符、换行符、中文全角空格等 | V | |
| \w | 字母、数字、下划线或汉字 | V | |
| ^ | 匹配字符串的开始 | V | |
| $ | 匹配字符串的结束 | V | |
| \ | 如果需要查找元字符本身,请使用字符转义,例如\..\\.\* |
V | |
| * | 匹配字符可以重复任意次数 | V | |
| + | 匹配字符一次或更多次 | V | |
| ? | 匹配字符零次或一次 | V | |
| {n} | 匹配字符n次 | V | |
| {n,} | 匹配字符n次货更多次 | V | |
| {x,y} | 匹配至少x次,最多y次字符 | V | |
| [aeiou] | 匹配aeiou中的任一字符 | V |
通过正则表达式字符串的格式为 元字符 匹配数量、字符 匹配数量或匹配字符 匹配数量
注意:’(‘和’)’都是元字符,单独在正则表达式中使用元字符只会匹配一次
学习完上面的正则表达式,我们可以写出这样复杂的表达式1
\(?0\d{2}[)-]?\d{8}
可以匹配(010-88886666)这样的电话号码
分支
不过刚才的正则表达式在某些情况下也会出现错误,比如010)11112222或(010-33334444
这或许是我们不想看到的,所以我们需要在适当的时候选择分支条件
采用|把不同的规则分开
比如:0\d{2}-\d{8}|0\d{3}-\d{7}可以匹配三位区号8位本地号和4位区号7为本地号
我们来改写一下上面的匹配电话号码的,以3位区号为例:\(?0\d{2}\)?[- ]?\d{8}|0\d{2}[- ]?\d{8}匹配3位区号,本地号码为8位的,注意-后有一个空格,若中间必须有一个-,可直接写-,也可单独直接写空格
分组
在前面我们知道怎么重复单个字符,如果需要重复多个字符就会用到分组
(\d{1,3}).){3}\d{1,3}是一个简单的IP地址匹配,不完全正确,我们来展开\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1.3},第一个分组重复3次,再与后面相连
为什么说不完全正确.0.0.0.0或256.256.256.256这些不满足条件的IP地址
这一次我们慢慢来写:
2[0-4]\d|25[0-5]|[01]?\d\d?匹配0~255((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}匹配前面的255.255.255.
剩下的就能搞定了吧
反义
取匹配的反义,比如匹配除了数字以外的字符
| 元字符 | 说明 | 匹配字符 | 匹配数量 |
|---|---|---|---|
| \W | 匹配任一不是字母、数字、下划线和汉字的字符 | V | |
| \S | 匹配任意不是空白符的字符 | V | |
| \D | 匹配任意不是数字的字符 | V | |
| \B | 匹配不是单词开头或结束的位置 | V | |
| [^x] | 匹配除了x以外的任一字符 | V | |
| [^aeiou] | 匹配除了aeiou这几个字母以外的任一字符 | V |
<a[^>]+>匹配用尖括号括起来的以a开头的字符串
后向引用
我们在前面的分组中使用了()来匹配文本,()内的内容为一个子表达式,默认情况下,每一个分组都拥有一个组号,从左向右,以1开始,自动递增
例如:\b(\w+)\b\s+\1\b可以用来匹配重复的单词go go或kitty kitty,其中的\1代表第一个分组中的正则表达式
也自己设置分组的组名,请使用(?<word>\w+)这样的语法,组名被指定为word了,或者这样(?'word'\w+).当需要引用这个分组匹配的内容时,你可以使用\k<word>.
所以上一个例子也可以写成\b(?<myWord>\w+)\b\s+\k<myWord>\b.
常用分组语法:
| 分类 | 语法 | 说明 |
|---|---|---|
| 匹配 | (exp) |
匹配exp,并捕获文本到自动命令的组里 |
| - | (?<name>exp) |
匹配exp,并捕获文本到名称为name的组里,或者写成(?'name'exp) |
| - | (?:exp) |
匹配exp,不捕获匹配的文本,也不给此分组分配组号 |
| 零宽断言 | (?=exp) |
匹配exp前面的位置 |
| - | (?<=exp) |
匹配exp后面的位置 |
| - | (?!exp) |
匹配后面跟的不是exp的位置 |
| - | (?<!exp) |
匹配前面不是exp的位置 |
| 注释 | (?#comment) | 提供注释让人阅读 |
(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号,有时候或许你真的需要
零宽断言
用于查找某些内容之前或之后的东西,也就是说它们像\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),recording code for developer,匹配到cording.((?<=\d)\d{3})+\b可以给很长的数字中每三位间加一个逗号(?<=\s)\d+(?=\s)匹配以空白符间隔的数字
使用网页测试工具测试发现,(?<=exp)走不通,OC测试正确
负向零宽断言
前面提到过怎么查找不是某个字符或不在某个字符串里的字符的方法.但是如果我们只是想要确保某个字符没有出现,但并不像去匹配它时怎么办?例如,我们找查找单词,里面出现了字母q,但是q后面跟的不是字母u,我们可以这样写:\b\w*q[^u]\w*\b.但是这样做并不完全正确,比如benq也会匹配到或者xxxq goging.
这个时候我们就可以使用负向零宽断言来匹配,因为只匹配位置.不捕获字符.\b\w*q(?!u)\w*\b
零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp.比如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字,\b((?!abc)\w)+\b匹配不含连续字符abc的单词
同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的后面不能匹配表达式exp.比如:(?<![a-z]\d{7})匹配前面不是小写字母的七位数字.
来一个更复杂的比如:(?<=<(\w+)>).*(?=<\/\1>),来看看(?<=<(\w)>)匹配<a>这样开始,
.*中间包含任何内容,(?=<\/\1>匹配<\a>这样的,\1反向引用,\w前后一致,相当于匹配简单的html标签内容.
注释
大概可以写成这样:
1 | (?<= # 断言要匹配的文本的前缀 |
贪婪与懒惰
当正则表达式中包含能接收重复的元字符时,通常在使整个表达式能匹配到的前提下尽可能匹配多的字符.
以这样的表达式为例:a.*b,它能匹配最长的以a开始,以b结束的字符串.如果用来搜索aabaab的话,他会匹配整个字符串aabaab.这被称为贪婪匹配.
有时候我们希望懒惰匹配,匹配更少的字符,这样得到的结果可能会更多.我们可以在重复的元字符后添加?.像这样a.*?b,这样就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的字符.我们得到的结果会是aab aab,字符更少,但可能结果会更多.
| 代码/语法 | 说明 |
|---|---|
| *? | 重复任意次数,尽可能少重复 |
| +? | 重复1次或更多次,尽可能少重复 |
| ?? | 重复0次或更多次,尽可能少重复 |
| {n,m}? | 重复n到m此,尽可能少重复 |
| {n,}? | 重复n次或更多次,尽可能少重复 |
特殊的元字符
| 代码/语法 | 说明 | |
|---|---|---|
| \a | 报警字符(打印它的效果是电脑嘀一声) | |
| \b | 通常是单词分界位置,但如果在字符类里使用代表退格 | |
| \t | 制表符,Tab | |
| \r | 回车 | |
| \v | 竖向制表符 | |
| \f | 换页符 | |
| \n | 换行符 | |
| \e | Escape | |
| \0nn | ASCII代码中八进制代码为nn的字符 | |
| \xnn | ASCII代码中十六进制代码为nn的字符 | |
| \unnnn | Unicode代码中十六进制代码为nnnn的字符 | |
| \cN | ASCII控制字符.比如\cC代表Ctrl+C | |
| \A | 字符串开头(类似^.但不受处理多行选项的影响) | |
| \Z | 字符串结尾或行尾(不受处理多行选项的影响) | |
| \z | 字符串结尾(类似$.但不受处理多行选项的影响) | |
| \G | 当前搜索的开头 | |
| \p{name} | Unicode中命名为name的字符类.例如\p{IsGreek} | |
| (?>exp) | 贪婪子表达式 | |
| (? |
平衡组 | |
| (?im-nsx:exp) | 在子表达式exp中改变处理选项 | |
| (?im-nsx) | 为表达式后面的部分改变处理选项 | |
| (?(exp)yes | no) | 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no |
| (?(exp)yes) | 同上,只是使用空表达式作为no | |
| (?(name)yes | no) | 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no |
| (?(name)yes) | 同上,只是使用空表达式作为no |