我所知道的正则表达式

我所知道的正则表达式(1)- 基础知识

2011年12月16日 没有评论

本文作为温故系列第一篇,主要帮助我回忆正则表达式的基础知识。

正则表达式是处理文本时的有力工具,能够按照一定的模式或规则去匹配具有一定格式的文本,并可以完成分组、替换等复杂功能。作为一程序员,正则作为必备武器,能给我们处理文带来极大的便利。经常用到的正则的场景如:javascript验证输入格式(密码、电话、email等)、网页爬虫从网页中的解析出需要的内容,替换相同模式的文本内容等。

正则中预定义了一些元字符,用来表示字符、个数、重复次数、逻辑关系等,以下是大致介绍:

字符匹配

. 匹配任意一个字符
^ 行开头
$ 行结束
\ 转义符
\d 一个数字,相当于[0-9]
\s 一个空白字符,相当于[ \t\n\x0B\f\r]
\w 一个单词中的字符:字母、数字、下划线,相当于[a-zA-Z_0-9]
\b 单词的边界(单词的开头或结尾)
[] []里面的任意一个字符

数量匹配,用来匹配字符出现的次数

x* 0个或者任意个x,
x? 0个或这1个x
x+ 至少1个x
x{n} n个x
x{n,} 至少n个x
x{n,m} 至少n个x,至多m个x

注:此为贪婪模式,就是匹配最多的结果。例如有个字符串abcdefg#aaa, [a-z]+的第一次匹配结果为abcdefg,不会是a或者ab等

[]的使用举例

[0-9] 一个数字
[^0-9] 一个非数字字符
[a-z] a-z中的任意一个字母(所有小写字母)
[A-Z] A-Z中的任意一个字母(所有大写字母)
[a-zA-Z] 所有字母的一个
[a-zA-Z0-9] 所有字母和数字中的一个
[a-ce-z] a-c和e-z中的一个,不含d
[a-d[m-p]] a-  d或者m-p中的一个,这个不是所有解释器都支持

注:在[]中的^不是表示行开头,而是标识反义,意为“除xxx之外的所有”

逻辑关系

XY X后面是Y
X|Y X或者Y

分组

在一个正则表达式中,将局部用()包含成子表达式,目的是对子表达式进行重复匹配等操作,每个()就是一个组。在匹配的过程中,会将匹配每个子表达式字符按顺序保存在临时变量中,最多可保存9个分组,可以使用1到9来引用。
例如有这样文本,要求匹配出网站名称和链接:
<a href=”http://www.google.com”>谷歌</a><a href=”http://www.baidu.com”>百度</a>

就可以使用分组,正则为:<a href=”([^\"]+)”>([^<]+)</a> ,每个括号为一组,共2个分组。
看下面的Java的测试用例:

        
        
1
2
3
4
5
6
7
8
        
        
package   org . buzheng . test ;
import   java . util . regex . Matcher ;
import   java . util . regex . Pattern ;
 
public   class   Test   {
     public   static   void   main ( String [ ]   args )   throws   Exception   {
         String   value   =   "<a href=" \ "http://www.google.com\"" > < / a > < a   href = "\"http://www.baidu.com\"" > < / a > " ;
         Pattern   pattern   =   Pattern . compile ( "

最终输出:
谷歌 – http://www.google.com
百度 – http://www.baidu.com

分组替换

分组可以很方便的完成替换,例如在apache的urlrewrite模块中,经常用到如下的正则:
RewriteRule ^/article/([0-9]+)$ /article.php?id=$1 [L]
就是通过分组1获取到id,从而通过$1赋值到后面的url中去

不捕获分组(2011-12-16添加)

(?:group-regex) 按分组(group-regex)来匹配,但不捕获该分组
(?<name>group-regex) 命名分组为name, 可以通过此名字捕获该分组


我所知道的正则表达式(2)- 贪婪模式与懒惰模式

2011年12月25日 没有评论

前段时间对自己以前了解的正则表达式的基础知识做了一个总结,有了基础知识已经能够能应付大多数情况,像字符串匹配、正则替换、校验等;但是再使用的过程中你可能发现一个问题,就是当使用重复元字符匹配数量时,总是会尽可能长的去匹配,而有时这恰恰不是你想要的。看下面的例子:

<a href=”http://www.google.com”>谷歌</a><a href=”http://www.baidu.com”>百度</a>

你希望匹配出每个链接的html内容。如果你用 <a (.*)</a>会首先匹配到整个字符串,而不是你希望的<a href=”http://www.google.com”>谷歌</a>和<a href=”http://www.baidu.com”>百度</a>。

为什么会出现这种情况呢?这就是正则表达式的贪婪模式。当出现重复数量的时候,会尽可能的多匹配。上述的正则表达式中 . 表示任意字符,* 代表可以重复出现任意个,根据正则表达式的贪婪个性,不匹配到最后才怪呢。就像人一样,有贪婪就有懒惰,一个?就可以让正则立刻改变本性,这时的正则就表现懒惰模式的本性了。

修改后的正则表达式为:<a (.*?)</a>,这时就可以匹配出每个连接的html了。

测试用例为:

         
         
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
         
         
package   org . buzheng . test ;
 
import   java . util . regex . Matcher ;
import   java . util . regex . Pattern ;
 
public   class   Test   {
     public   static   void   main ( String [ ]   args )   throws   Exception   {
         String   value   =   "<a href=\"http://www.google.com\">谷歌</a><a href=\"http://www.baidu.com\">百度</a>" ;
 
         System . out . println ( "贪婪模式:" ) ;
         Pattern   pattern   =   Pattern . compile ( "<a (.*)</a>" ) ;
         Matcher   matcher   =   pattern . matcher ( value ) ;
         while   ( matcher . find ( ) )   {
             System . out . println ( matcher . group ( 0 ) ) ;
         }
 
         System . out . println ( "贪婪模式:" ) ;
         pattern   =   Pattern . compile ( "<a (.*?)</a>" ) ;
         matcher   =   pattern . matcher ( value ) ;
         while   ( matcher . find ( ) )   {
             System . out . println ( matcher . group ( 0 ) ) ;
         }
     }
}

最后总结一下数量元字符懒惰模式的常用写法(其实就是多了一个?):

x*? 0个或者任意个x, 最少匹配
x?? 0个或这1个x, 最少匹配
x+? 至少1个x, 最少匹配
x{n,}? 至少n个x, 最少匹配
x{n,m}? 至少n个x,至多m个x, 最少匹配


我所知道的正则表达式(3)- 零宽断言

2011年12月31日 没有评论

前段时间总结了正则表达式的基础知识以及贪婪模式和懒惰模式,今天再总结一下零宽断言。

正则表达式里面比较高级的应用就属于零宽断言了。那么什么是零宽断言呢?拆分法从字面上分析一下,零宽,即宽带为0,意味者不会返回匹配的字符,以为匹配的是当前字符的位置。断言,就是预言、假设,意味着从此处假设存在什么情况。那么零宽断言的意思就是假定从此位置开始满足某种情况。

根据断言字符串位于当前位置的前后关系,分为正向和反向断言,根据断言肯定和否定的语气,又有正向否定断言和反向否定断言。肯定即断言存在该字符串、否定即相反的意思:存在的不是该字符串,总之概念比较绕口,下表介绍的时候顺便给出英文:

(?=X) 正向断言,假定该位置后跟的是X
zero-width positive lookahead
(?!X) 正向否定断言,假设该位置后跟的不是X
zero-width negative lookahead
(?<=X) 反向断言,假设该位置前跟的是X
zero-width positive lookbehind
(?<!X) 反向否定断言,假设该位置前跟的不是X
zero-width negative lookbehind

举例:
(?=X) 正向断言

[^\s]+?(?=ing) 来匹配 having doing listing,会匹配出 hav, do, list,注意:并不会匹配出ing,因为ing是零宽断言的部分。

(?!X) 正向否定断言
这个可参考以前的文章:一个匹配数字和字母密码的正则表达式

(?<=X) 反向断言
(?<=hell)[a-z]+ 来匹配test hellen hellas helloween,会匹配出 en, as, oween

(?<!X) 反向否定断言
[a-z]+(?<!hell)en 来匹配testen hellen hellas helloween,会匹配出testen和helloween

注:所有的案例都在UE下进行测试。


一个匹配数字和字母密码的正则表达式

2011年12月14日 没有评论

一个用户注册功能的密码有如下要求:由数字和字母组成,并且要同时含有数字和字母,且长度要在8-16位之间。

如何分析需求?拆分!这就是软件设计的一般思路了。于是乎,拆分需求如下:
1,不能全部是数字
2,不能全部是字母
3,必须是数字或字母
只要能同时满足上面3个要求就可以了,写出来如下:

          
          
1
          
          
^ ( ? ! [ 0-9 ] + $ ) ( ? ! [ a-zA-Z ] + $ ) [ 0-9A-Za-z ] { 8 , 16 } $

分开来注释一下:
^ 匹配一行的开头位置
(?![0-9]+$) 预测该位置后面不全是数字
(?![a-zA-Z]+$) 预测该位置后面不全是字母
[0-9A-Za-z] {8,16} 由8-16位数字或这字母组成
$ 匹配行结尾位置

注:(?!xxxx) 是正则表达式的负向零宽断言一种形式,标识预该位置后不是xxxx字符。

测试用例如下:

          
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
          
          
public   class   Test   {
     public   static   void   main ( String [ ]   args )   throws   Exception   {
         String   regex   =   "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$" ;         
 
         String   value   =   "aaa" ;    // 长度不够
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "1111aaaa1111aaaaa" ;    // 太长
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "111111111" ;   // 纯数字
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "aaaaaaaaa" ;   // 纯字母
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "####@@@@#" ;   // 特殊字符
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "1111aaaa" ;    // 数字字母组合
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "aaaa1111" ;   // 数字字母组合
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "aa1111aa" ;      // 数字字母组合
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "11aaaa11" ;      // 数字字母组合
         System . out . println ( value . matches ( regex ) ) ;
 
         value   =   "aa11aa11" ;   // 数字字母组合
         System . out . println ( value . matches ( regex ) ) ;
     }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值