关闭

我所知道的正则表达式

标签: 正则表达式regex百度string测试javascript
191人阅读 评论(0) 收藏 举报
分类:

我所知道的正则表达式(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));
    }
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:125111次
    • 积分:1552
    • 等级:
    • 排名:千里之外
    • 原创:7篇
    • 转载:208篇
    • 译文:0篇
    • 评论:6条