在JDK 1.4中,Java增加了对正则表达式的支持。

java与正则相关的工具主要在java.util.regex包中;此包中主要有两个类:Pattern、Matcher。


Pattern类

声明:public final class Patternimplements java.io.Serializable

Pattern类有final 修饰,可知他不能被子类继承。

含义:模式类,正则表达式的编译表示形式。

注意:此类的实例是不可变的,可供多个并发线程安全使用。

构造器

private Pattern(String p, int f) {
        pattern = p;
        flags = f;

        // Reset group index count
        capturingGroupCount = 1;
        localCount = 0;

        if (pattern.length() > 0) {
            compile();
        } else {
            root = new Start(lastAccept);
            matchRoot = lastAccept;
        }
    }

构造器是私有的,可知不能通过new创建Pattern对象。

如何得到Pattern类的实例?

查阅所有方法后发现:

public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }

    public static Pattern compile(String regex, int flags) {
        return new Pattern(regex, flags);
    }

可知是通过Pattern调用静态方法compile返回Pattern实例。


其他部分方法: 

1、public Matcher matcher(CharSequence input)

创建匹配给定输入与此模式的匹配器,返回此模式的新匹配器。

public Matcher matcher(CharSequence input) {
    if (!compiled) {
        synchronized(this) {
        if (!compiled)
            compile();
        }
    }
        Matcher m = new Matcher(this, input);
        return m;
    }


2、public static boolean matches(String regex,CharSequence input)


编译给定正则表达式并尝试将给定输入与其匹配。

public static boolean matches(String regex, CharSequence input) {
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(input);
        return m.matches();
    }


测试:


代码1(参考JDK API 1.6例子):

        Pattern p = Pattern.compile("a*b");
        Matcher m = p.matcher("aaaaab");
        boolean b = m.matches();
        System.out.println(b);// true

代码2:

        System.out.println(Pattern.matches("a*b", "aaaaab"));// true

查阅matcher和matches方法可知matches自动做了一些处理,代码2可视为代码1的简化,他们是等效的。

如果要多次使用一种模式,编译一次后重用此模式比每次都调用此方法效率更高。

3、public String[] split(CharSequence input) 和 public String[] split(CharSequence input, int limit)

input:要拆分的字符序列;

limit:结果阈值;

根据指定模式拆分输入序列。

limit参数作用:

limit参数控制应用模式的次数,从而影响结果数组的长度。

如果 n 大于零,那么模式至多应用 n- 1 次,数组的长度不大于 n,并且数组的最后条目将包含除最后的匹配定界符之外的所有输入。

如果 n 非正,那么将应用模式的次数不受限制,并且数组可以为任意长度。

如果 n 为零,那么应用模式的次数不受限制,数组可以为任意长度,并且将丢弃尾部空字符串。 

查看split(CharSequence input) 源码: 

public String[] split(CharSequence input) {
        return split(input, 0);
    }

可知split(CharSequence input)实际调用了split(CharSequence input, int limit);以下只讨论split(CharSequence input, int limit)。

假设:

若input="boo:and:foo",匹配符为"o",可知模式最多可应用4次,数组的长度最大为5;

1、当limit=-2时,应用模式的次数不受限制且数组可以为任意长度;推测模式应用4次,数组的长度为5,数组为{"b","",":and:f","",""};

2、当limit=2时,模式至多应用1次,数组的长度不大于 2,且第二个元素包含除最后的匹配定界符之外的所有输入;推测模式应用1次,数组的长度为2,数组为{"b","o:and:foo"};

3、当limit=7时,模式至多应用6次,数组的长度不大于 7;推测模式应用4次,数组的长度为5,数组为{"b","",":and:f","",""};

4、当limit=0时,应用模式的次数不受限制,数组可以为任意长度,并且将丢弃尾部空字符串;推测模式应用4次,数组的长度为3,数组为{"b","",":and:f"}。

代码验证:

public static void main(String[] args) {
        String[] arr = null;
        CharSequence input = "boo:and:foo";
        Pattern p = Pattern.compile("o");
        arr = p.split(input, -2);
        System.out.println(printArr(arr));// {"b","",":and:f","",""},共有5个元素
        arr = p.split(input, 2);
        System.out.println(printArr(arr));// {"b","o:and:foo"},共有2个元素
        arr = p.split(input, 7);
        System.out.println(printArr(arr));// {"b","",":and:f","",""},共有5个元素
        arr = p.split(input, 0);
        System.out.println(printArr(arr));// {"b","",":and:f"},共有3个元素
    }

    // 打印String数组
    public static String printArr(String[] arr) {
        int length = arr.length;
        StringBuffer sb = new StringBuffer();
        sb.append("{");
        for (int i = 0; i < length; i++) {
            sb.append("\"").append(arr[i]).append("\"");
            if (i != length - 1)
                sb.append(",");
        }
        sb.append("}").append(",共有" + length + "个元素");
        return sb.toString();
    }

输出结果与以上猜测结果一致。

4、toString()和pattern()

两个方法代码一样,都是返回此模式的字符串表示形式。

public String toString() {
        return pattern;
    }

    public String pattern() {
        return pattern;
    }

测试:

Pattern p = Pattern.compile("\\d+");
System.out.println(p.toString());// 输出\d+
System.out.println(p.pattern());// 输出\d+

5、public int flags()

返回此模式的匹配标志。

public int flags() {
        return flags;
    }

测试:

Pattern p = Pattern.compile("a+", Pattern.CASE_INSENSITIVE);
System.out.println(p.flags());// 2

Matcher类

1.Pattern.matcher(String regex,CharSequence input)是一个静态方法,用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串.

Java代码示例:

Pattern.matches("\\d+","2223");//返回true

Pattern.matches("\\d+","2223aa");//返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到

Pattern.matches("\\d+","22bb23");//返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到

2.Pattern.matcher(CharSequence input)

说了这么多,终于轮到Matcher类登场了,Pattern.matcher(CharSequence input)返回一个Matcher对象.
Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例. 
Pattern类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将Pattern与Matcher一起合作.Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持. 

Java代码示例:

Pattern p=Pattern.compile("\\d+");

Matcher m=p.matcher("22bb23");

m.pattern();//返回p 也就是返回该Matcher对象是由哪个Pattern对象的创建的

3.Matcher.matches()/ Matcher.lookingAt()/ Matcher.find()

Matcher类提供三个匹配操作方法,三个方法均返回boolean类型,当匹配到时返回true,没匹配到则返回false 

matches()对整个字符串进行匹配,只有整个字符串都匹配了才返回true 

Java代码示例:

Pattern p=Pattern.compile("\\d+");

Matcher m=p.matcher("22bb23");

m.matches();//返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功.

Matcher m2=p.matcher("2223");

m2.matches();//返回true,因为\d+匹配到了整个字符串

我们现在回头看一下Pattern.matcher(String regex,CharSequence input) ,它与下面这段代码等价 
Pattern.compile(regex).matcher(input).matches()

lookingAt()对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true 

Java代码示例:

Pattern p=Pattern.compile("\\d+");

Matcher m=p.matcher("22bb23");

m.lookingAt();//返回true,因为\d+匹配到了前面的22

Matcher m2=p.matcher("aa2223");

m2.lookingAt();//返回false,因为\d+不能匹配前面的aa

find()对字符串进行匹配,匹配到的字符串可以在任何位置. 

Java代码示例:

Pattern p=Pattern.compile("\\d+");

Matcher m=p.matcher("22bb23");

m.find();//返回true

Matcher m2=p.matcher("aa2223");

m2.find();//返回true

Matcher m3=p.matcher("aa2223bb");

m3.find();//返回true

Matcher m4=p.matcher("aabb");

m4.find();//返回false

4.Mathcer.start()/ Matcher.end()/ Matcher.group()

当使用matches() lookingAt() find()执行匹配操作后,就可以利用以上三个方法得到更详细的信息. 

    start()返回匹配到的子字符串在字符串中的索引位置. 

    end()返回匹配到的子字符串的最后一个字符在字符串中的索引位置. 

    group()返回匹配到的子字符串 

Java代码示例:

Pattern p = Pattern.compile("\\d+");<br>       

Matcher m1 = p.matcher("aaa2223bb");

System.out.println(m1.find());// 匹配2223【true】

System.out.println(m1.start());// 返回3

System.out.println(m1.end());// 返回7,返回的是2223后的索引号

System.out.println(m1.group());// 返回2223<br>

Matcher m2 = p.matcher("2223bb");

System.out.println(m2.lookingAt()); // 匹配2223【true】

System.out.println(m2.start()); // 返回0,由于lookingAt()只能匹配前面的字符串,所以当使用lookingAt()匹配时,start()方法总是返回0

System.out.println(m2.end()); // 返回4

System.out.println(m2.group()); // 返回2223<br>

Matcher m3 = p.matcher("2223bb");

System.out.println(m3.matches()); // 匹配整个字符串【false】

System.out.println(m3.start()); // java.lang.IllegalStateException【m3.matches()为false】

System.out.println(m3.end()); // java.lang.IllegalStateException【m3.matches()为false】

System.out.println(m3.group()); // java.lang.IllegalStateException【m3.matches()为false】

说了这么多,相信大家都明白了以上几个方法的使用,该说说正则表达式的分组在java中是怎么使用的. 
start() end() , group()均有一个重载方法它们是start(int i) , end(int i) group(int i)专用于分组操作,Mathcer类还有一个groupCount()用于返回有多少组. 

Java代码示例:

Pattern p = Pattern.compile("([a-z]+)(\\d+)");

Matcher m = p.matcher("aaa2223bb");

System.out.println(m.find()); // 匹配aaa2223

System.out.println(m.groupCount()); // 返回2,因为有2组

System.out.println(m.start(1)); // 返回0 返回第一组匹配到的子字符串在字符串中的索引号

System.out.println(m.start(2)); // 返回3

System.out.println(m.end(1)); // 返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置.

System.out.println(m.end(2)); // 返回7

System.out.println(m.group(1)); // 返回aaa,返回第一组匹配到的子字符串

System.out.println(m.group(2)); // 返回2223,返回第二组匹配到的子字符串

现在我们使用一下稍微高级点的正则匹配操作,例如有一段文本,里面有很多数字,而且这些数字是分开的,我们现在要将文本中所有数字都取出来,利用java的正则操作是那么的简单. 

Java代码示例:

Pattern p=Pattern.compile("\\d+");

Matcher m=p.matcher("我的QQ是:456456 我的电话是:0532214 我的邮箱是:aaa123@aaa.com");

while(m.find()) {

  System.out.println(m.group());

}

输出:

456456

0532214

123