Java正则表达式

正则表达式是一种对文本进行搜索和操作的强大工具。
java.util.regex包提供了Java对于正则表达式功能的支持,其功能主要由Pattern和Matcher两个类完成。

Pattern类:

Pattern类用于编译和解析正则表达式
1、获取Pattern对象
由于没有公共的构造函数,只能通过Pattern的compile静态方法获取一个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(String regex,int flags)方法需要传入两个参数,第一个参数regex是需要编译和解析的正则表达式字符串,第二个参数flags表示正则表达式匹配过程中的规则标志的和(这些标志是Pattern的常量。例如CASE_INSENSITIVE的值为0x02,表示匹配过程中不区分大小写,COMMENTS的值为0x04,表示正则表达式中包含的空白符不会与测试字符序列的空白符匹配。如果需要设置这两种规则,flags传入他们的和6就行了。),如没有特殊要求,传入0就行。

/**
* 获取Pattern对象
*/
Pattern pattern = Pattern.compile("\\d{3}");
2、Pattern的主要方法(方法名前带“*”的为静态方法)

(1)matcher(CharSequence input):该方法用于获取Matcher对象(Matcher类后面会讲到),需要传入待测试的字符序列(实现了CharSequence接口的类型都可以,例如:StringBuffer、String)。

    public static void main(String[] args){
        // 获取Pattern对象
        Pattern pattern = Pattern.compile("\\d{3}");
        //获取Matcher对象
        Matcher matcher = pattern.matcher("1234567");

    }

(2)*matches(String regex, CharSequence input):该方法用于判断字符序列是否完全匹配正则表达式。第一个参数为表示正式表达式的字符串,第二个参数同matcher(CharSequence input)方法中的参数。

    /**
    * 可以看到该方法先后创建了Pattern和Matcher对象,并最后调用了Matcher对象的matches()方法
    * 这个方法一般用于只需要进行一次判断的需求
    * Matcher的matches()方法后面会讲到,现在只需要知道一点:
    * 待测试的字符序列必须完全匹配正则表达式,不能多也不能少
    * ,
    */
    public static boolean matches(String regex, CharSequence input) {
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(input);
        return m.matches();
    }

现在做一个测试:

public static void main(String[] args){
    System.out.println(Pattern.matches("\\d{3}", "123"));
    System.out.println(Pattern.matches("\\d{3}", "a123"));
    System.out.println(Pattern.matches("\\d{3}", "1233"));
}

打印的结果分别是true、false、false。
(3)public String[] split(CharSequence input, int limit):该方法用于根据正则表达式将传入的字符序列input分割为字符串数组,limit表示字符串数组的最大长度。(String类的split()方法最后也是通过调用该方式来实现分割功能的)现在来做个测试:

    /**
    *   打印结果:
    * array1:[a, 4bc, def, gh]
    * array2:[a1234bc567def890gh]
    * array3:[a, 4bc, def890gh]
    * 
    */
    public static void main(String[] args) {
        // 获取Pattern对象
        Pattern pattern = Pattern.compile("\\d{3}");
        // 对传入的字符串序列进行分割操作
        String[] array1 = pattern.split("a1234bc567def890gh", 0);
        String[] array2 = pattern.split("a1234bc567def890gh", 1);
        String[] array3 = pattern.split("a1234bc567def890gh", 3);
        System.out.println("array1:" + Arrays.toString(array1));
        System.out.println("array2:" + Arrays.toString(array2));
        System.out.println("array3:" + Arrays.toString(array3));

    }

(4)*String quote(String s):将传入的字符串转变为它的字面量,不带有任何特殊含义(比如元字符、转义字符、)。类似于xml中的CDATA,用CDATA定义的文本不会被xml解析器解析,而被当作普通文本。

    /**
    * 打印结果:
    * \Q\d\E
    * \Qa*\E
    * 可以看到调用quote方法后,原正则表达式被“\Q”和“\E”包裹了起来,而且转义字符\也被去掉了
    * 如果我们需要精确匹配的时候,可以用到quote方法
    */
    public static void main(String[] args) {
        System.out.println(Pattern.quote("\\d"));
        System.out.println(Pattern.quote("a*"));
    }

2、Matcher类:

Matcher类主要用于完成字符序列的匹配操作。
1、获取Matcher对象
由于Matcher类也没有公共的构造器,只能通过Pattern对象的matcher()方法获取
        // 获取Pattern对象
        Pattern pattern = Pattern.compile("\\d{3}");
        //获取Matcher对象
        Matcher matcher = pattern.matcher("1234567");
2、Matcher的主要方法

讲Matcher类的方法之前,先谈谈Matcher两种功能(姑且取名功能吧):

1、正则表达式匹配字符序列:对应方法match(int from, int anchor),匹配返回true,不匹配返回false,其中包括两种模式(anchor):
(1)正则表达式完全匹配字符序列:对应Matcher类中的常量ENDANCHOR ,即字符序列不能有多余的字符,实现的公共方法方法为matches()
(2)正则表达式不必完全匹配字符序列:对应Matcher类中的常量NOANCHOR,即字符序列可以有多余的字符,但是多余的字符必须在匹配字符序列的后面,实现的公共方法为lookingAt()

2、在字符序列中搜索匹配正则表达式的字符序列:对应方法search(int from),搜索到后返回false,没有搜索到返回false,默认使用的NOANCHOR模式。实现的公共方法为find()。

由于match和search方法只有包访问权限,我们可以通过matches()、lookingAt()、find()这三个方法来测试。
先测试matches()、lookingAt()方法:

    /**
     * 打印结果:
     *  matcher1 Match(ENDANCHOR): true
     *  matcher2 Match(ENDANCHOR): false
     *  matcher3 Match(ENDANCHOR): false
     *  ---------------------------------------
     *  matcher1 Match(NOANCHOR): true
     *  matcher2 Match(NOANCHOR): false
     *  matcher3 Match(NOANCHOR): true
     *
     * 分析:
     * 1、ENDANCHOR模式下,只有matcher1能匹配成功
     * 2、NOANCHOR模式下,匹配的序列前面有其他字符时返回false,而后面有其他字符时返回true
     * 3、相同Matcher对象相同模式下匹配多次,返回结果相同
     * 结论:
     * 1、match()方法每次都是从字符序列的第一个字符开始匹配
     * 2、ENDANCHOR模式下,正则字符串和字符序列必须完全匹配,不能有多的字符
     * 3、NOANCHOR模式下,正则字符串和字符序列不必须完全匹配,可以有其他的字符,但是其他字符必须位于匹配字符序列的后面
     */
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("123");
        Matcher matcher1 = pattern.matcher("123");
        Matcher matcher2 = pattern.matcher("abc123");
        Matcher matcher3 = pattern.matcher("123abc");

        System.out.println("matcher1 Match(ENDANCHOR): " + matcher1.matches());
        System.out.println("matcher2 Match(ENDANCHOR): " + matcher2.matches());
        System.out.println("matcher3 Match(ENDANCHOR): " + matcher3.matches());
        System.out.println("---------------------------------------");
        System.out.println("matcher1 Match(NOANCHOR): " + matcher1.lookingAt());
        System.out.println("matcher2 Match(NOANCHOR): " + matcher2.lookingAt());
        System.out.println("matcher3 Match(NOANCHOR): " + matcher3.lookingAt());
    }

接下来测试find()方法:

    /**
     *打印结果:
     *  第1次搜索匹配成功!
     *  第2次搜索匹配成功!
     *  第3次搜索匹配成功!
     *
     *结果分析:
     *  搜索匹配成功三次,测试字符序列也正好有三个“123”
     *
     *结论:
     *  1、find()方法时只会对字符序列进行一次从头到尾的搜索匹配工作
     *  2、搜索过程中匹配成功一次后会停止搜索,直到下一次调用find()
     *  3、搜索到结尾时停止搜索,如果没有匹配到,返回false
     */
    private static void testFind() {
        Pattern pattern = Pattern.compile("123");
        Matcher matcher = pattern.matcher("abc123def123ghi123jklmn");

        int number = 0;
        while (matcher.find()) {
            number++;
            System.out.println("第" + number + "次搜索匹配成功!");
        }
    }

在工作中,我们需要根据实际的情况来分别使用这几个方法。比如在验证客户登陆输入的验证码时,需要完全匹配才能验证通过,这是可以选择matches()方法(即match方法的ENDANCHOR模式)。在需要知道某段文本中符合正则表达式的序列有多少个,并且需要得到这些序列具体信息时,可以使用find()方法(获取具体信息的方法后面会讲到)。

为了更好的了解match和search方法,介绍下Matcher类中的两个属性first和last

    /**
     * The range of string that last matched the pattern. If the last
     * match failed then first is -1; last initially holds 0 then it
     * holds the index of the end of the last match (which is where the
     * next search starts).
     */
    int first = -1, last = 0;
first:搜索和匹配过程中,存储匹配的序列的第一个字符的索引,初始化和搜索匹配失败时,first=-1
last:搜索和匹配过程中,存储匹配的序列的最后一个字符的索引+1(也是下一次搜索和匹配过程的起点)

下面我们通过反射技术来查看这两个属性变化过程

    /**
     * 打印first、last属性的值
     * 
     * @param m
     * @throws Throwable
     */
    private static void printFieldValue(String status, Matcher m) throws Throwable {
        System.out.println("\n---" + status + "BEGIN------------------------------------------------------");
        for (Field field : m.getClass().getDeclaredFields()) {
            if (!field.getName().equals("first") && !field.getName().equals("last")) {
                continue;
            }
            field.setAccessible(true);// 取消访问权限检查,保证能够访问到属性
            System.out.println(field.getName() + ": " + field.get(m));
        }
        System.out.println("---" + status + "END------------------------------------------------------");
    }

    public static void main(String[] args) throws Throwable {
        Matcher matcher = Pattern.compile("123").matcher("123abc123defg123hijk123lmn");
        printFieldValue("初始化", matcher);

        int number = 0;
        while (matcher.find()) {
            number++;
            printFieldValue("第" + number + "次find()", matcher);
        }
        printFieldValue("结束", matcher);
    }

打印结果如下:

---初始化BEGIN------------------------------------------------------
first: -1
last: 0
---初始化END------------------------------------------------------

---第1次find()BEGIN------------------------------------------------------
first: 0
last: 3
---第1次find()END------------------------------------------------------

---第2次find()BEGIN------------------------------------------------------
first: 6
last: 9
---第2次find()END------------------------------------------------------

---第3次find()BEGIN------------------------------------------------------
first: 13
last: 16
---第3次find()END------------------------------------------------------

---第4次find()BEGIN------------------------------------------------------
first: 20
last: 23
---第4次find()END------------------------------------------------------

---结束BEGIN------------------------------------------------------
first: -1
last: 23
---结束END------------------------------------------------------

从结果结合find()原码可以看出find()的过程:

    /*
    * 方法的第一行就将last值赋值给了nextSearchIndex,nextSearchIndex即下一次搜索的起点
    */
 public boolean find() {
        int nextSearchIndex = last;
        if (nextSearchIndex == first)
            nextSearchIndex++;

        // If next search starts before region, start it at region
        if (nextSearchIndex < from)
            nextSearchIndex = from;

        // If next search starts beyond region then it fails
        if (nextSearchIndex > to) {
            for (int i = 0; i < groups.length; i++)
                groups[i] = -1;
            return false;
        }
        return search(nextSearchIndex);
    }
find()过程:
以last(即上一次搜索结束时的最后一个字符的索引+1)的索引位置作为起点开始搜索,当匹配到一个"123"时,会停止搜索,并更新first为“1”的索引,last为“3”的索引+1

其他的方法这里就不测试了,有兴趣的可以自己试试。

到这里,大家对Matcher类有了一定的了解了,下面开始其他的方法:
先看下面的一段代码

    public static void main(String[] args) {
        String regex = "(?<group1>t)o(?<group2>o?)\\b";
        String str = "Nice to meet you, my name is HanMeimei.My name is LiLei. Nice to meet you, too. Welcome!";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);
        System.out.println("groupCount:" + matcher.groupCount());

        StringBuffer sb = new StringBuffer();
        int number = 0;
        while (matcher.find()) {
            number++;
            System.out.println("\n--第" + number + "次BEGIN:");
            System.out.println("group():" + matcher.group() + "   group(1):" + matcher.group(1)
            + "   group(\"group1\"):" + matcher.group("group1"));
            System.out.println("start():" + matcher.start() + "   start(1):" + matcher.start(1)
                    + "   start(\"group1\"):" + matcher.start("group1"));
            System.out.println("end():" + matcher.end() + "   end(1):" + matcher.end(1) + "   end(\"group1\"):"
                    + matcher.end("group1"));
            matcher.appendReplacement(sb, "2222");
            System.out.println("appendReplacement:" + sb.toString() + "\n--第" + number
                    + "次END--------------------------------------------------------------------\n");
        }
        matcher.appendTail(sb);
        System.out.println("appendTail: " + sb);
        System.out.println("matchCount:" + number);
    }

打印结果:

groupCount:2

--第1次BEGIN:
group():to   group(1):t   group("group1"):t
start():5   start(1):5   start("group1"):5
end():7   end(1):6   end("group1"):6
appendReplacement:Nice 2222
--第1次END--------------------------------------------------------------------

--第2次BEGIN:
group():to   group(1):t   group("group1"):t
start():62   start(1):62   start("group1"):62
end():64   end(1):63   end("group1"):63
appendReplacement:Nice 2222 meet you, my name is HanMeimei.My name is LiLei. Nice 2222
--第2次END--------------------------------------------------------------------

--第3次BEGIN:
group():too   group(1):t   group("group1"):t
start():75   start(1):75   start("group1"):75
end():78   end(1):76   end("group1"):76
appendReplacement:Nice 2222 meet you, my name is HanMeimei.My name is LiLei. Nice 2222 meet you, 2222
--第3次END--------------------------------------------------------------------

appendTail: Nice 2222 meet you, my name is HanMeimei.My name is LiLei. Nice 2222 meet you, 2222. Welcome!
matchCount:3

先解释下这个正则表达式

\b(?<group1>t)o(?<group2>o?)\b:
1、\b:单词或者行的边界
2、(?<group1>t):捕获组,"t"是捕获组的正则表达式,“group1”是这个捕获组的名称,这个是1.8新增的功能,其用法为(?<groupName>)
3、该正则表达式匹配单词to或者too

1、groupCount():返回正则表达式中捕获组的数量

    \b(?<group1>t)o(?<group2>o?)\b 有两个捕获组(“group1”和“group2”),所以返回2

2、group():返回上一次匹配的字符串

第一次匹配的是第一个“to”,第三次捕获的是“too”

3、group(int group):返回上一次匹配中,第“group”个捕获组捕获的字符串

第一次匹配的是“to”,其中第一个捕获组捕获了“t”,返回“t”

4、group(String name):返回上一次匹配中,名称为“name”的捕获组捕获的字符串

第一次匹配的是“to”,其中名称为“group1”的捕获组捕获了“t”,返回“t”

5、int start():返回上一次匹配中,匹配的字符串的第一个字符的索引(即first的值)

第一次匹配的是“to”,第一个字符“t”的索引是5,返回5

6、start(int group):返回上一次匹配中,第“group”个捕获组捕获的字符串的第一个字符的索引

第一次匹配的是“to”,其中第一个捕获组捕获了“t”,第一个字符“t”的索引是5,返回5

7、start(String name):返回上一次匹配中,名称为“name”的捕获组捕获的字符串的第一个字符的索引

第一次匹配的是“to”,其中名称为“group1”的捕获组捕获了“t”,第一个字符“t”的索引是5,返回5

8、int end():返回上一次匹配中,匹配的字符串的最后一个字符的索引+1(即last的值)

第三次匹配的是“too”,最后一个字符“o”的索引是77,返回78

9、end(int group):返回上一次匹配中,第“group”个捕获组捕获的字符串的最后一个字符的索引+1

第三次匹配的是“too”,其中第一个捕获组捕获了“t”,最后一个字符“t”的索引是75,返回76

10、end(String name):返回上一次匹配中,名称为“name”的捕获组捕获的字符串

第三次匹配的是“too”,其中名称为“group1”的捕获组捕获了“t”,最后一个字符“t”的索引是75,返回76

11、appendReplacement(StringBuffer sb, String replacement):将上一次匹配过程中,搜索的字符串(即起点和结束位置之间的字符串)拼接到“sb”中,并用“replacement”替换匹配到的字符串
12、appendTail(StringBuffer sb):将剩下的未搜索的字符串拼接到“sb”中

这里介绍了一部分Matcher类的方法。
上面只提到了first和last两个属性,还有其他的一些属性,比如oldLast、lastAppendPosition等等,如果有兴趣可以使用反射技术查看这些属性在match()和search()两个方法调用过程中的变化,这些属性值的变化对于理解Matcher类的方法很重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值