Pattern和Matcher

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43719015/article/details/102646625

正则表达式

正则基础知识点
元字符

万物皆有缘,正则也是如此,元字符是构造正则表达式的一种基本元素。

我们先来记几个常用的元字符

.    匹配除换行符以外的任意字符
w    匹配字母或数字或下划线或汉字
s    匹配任意的空白符
d    匹配数字匹配单词的开始或结束
^    匹配字符串的开始
$    匹配字符串的结束

有了元字符之后,我们就可以利用这些元字符来写一些简单的正则表达式了,
比如:
匹配有abc开头的字符串:abc或者^abc
匹配8位数字的QQ号码:^dddddddd$
匹配1开头11位数字的手机号码:^1dddddddddd$

重复限定符

正则表达式中一些重复限定符,把重复部分用合适的限定符替代,下面我们来看一些限定符:

 *     重复零次或更多次
 +     重复一次或更多次
 ?     重复零次或一次
{n}    重复n次
{n,}   重复n次或更多次
{n,m}  重复n到m次

有了这些限定符之后,我们就可以对之前的正则表达式进行改造了,比如:
匹配8位数字的QQ号码:^d{8}$
匹配1开头11位数字的手机号码:^1d{10}$
匹配银行卡号是14~18位的数字:^d{14,18}$
匹配以a开头的,0个或多个b结尾的字符串^ab*$

分组

正则表达式中用小括号 () 来做分组,也就是括号中的内容作为一个整体。
因此当我们要匹配多个ab时,我们可以这样。
如匹配字符串中包含0到多个ab开头:^(ab)*

转义

我们看到正则表达式用小括号来做分组,那么问题来了:
如果要匹配的字符串中本身就包含小括号,那是不是冲突?应该怎么办?
针对这种情况,正则提供了转义的方式,也就是要把这些元字符、限定符或者关键字转义成普通的字符,做法很简答,就是在要转义的字符前面加个斜杠,也就是即可。
如要匹配以(ab)开头:^(\(ab\))*

条件或

我们都知道:国内号码都来自三大网,它们都有属于自己的号段,比如联通有130/131/132/155/156/185/186/145/176等号段,假如让我们匹配一个联通的号码,那按照我们目前所学到的正则,应该无从下手的,因为这里包含了一些并列的条件,也就是“或”,那么在正则中是如何表示“或”的呢?

正则用符号 | 来表示或,也叫做分支条件,当满足正则里的分支条件的任何一种条件时,都会当成是匹配成功。

那么我们就可以用“或”条件来处理这个问题:^(130|131|132|155|156|185|186|145|176)d{8}$

区间

看到上面的例子,是不是看到有什么规律?是不是还有一种想要简化的冲动?
实际是有的

正则提供一个元字符中括号 [] 来表示区间条件。

那上面的正则我们还改成这样:
^((13[0-2])|(15[56])|(18[5-6])|145|176)d{8}$

好了,正则表达式的基本用法就讲到这里了,其实它还有非常多的知识点以及元字符,我们在此只列举了部分元字符和语法来讲,旨在给那些不懂正则或者想学正则但有看不下去文档的人做一个快速入门级的教程,看完本教程,即使你不能写出高大上的正则,至少也能写一些简单的正则或者看得懂别人写的正则了。

Pattern类定义

public final class Pattern extends Object implementsSerializable
正则表达式的编译表示形式。用于编译正则表达式后创建一个匹配模式(Matcher)

说明:
指定为字符串的正则表达式必须首先被编译为此类(Pattern)的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器(Matcher)中,所以多个匹配器(Matcher)可以共享同一模式(Pattern)。

如:

        //创建模式对象
        Pattern p =Pattern.compile("a*b");
        //创建匹配器对象
        Matcher m =p.matcher("aaaaab");
        //校验是否匹配
        boolean b = m.matches();
        在仅使用一次正则表达式时,可以方便地通过此类定义matches方法。
        此方法编译表达式并在单个调用中将输入序列与其匹配。语句:
        //等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。
        boolean b =Pattern.matches("a*b", "aaaaab");     

        Pattern 此类的实例是不可变的,可供多个并发线程安全使用。Matcher类的实例用于此目的则不安全。

Pattern类方法详解

1、Pattern complie(String regex):编译正则表达式,并创建Pattern类。

由于Pattern的构造函数是私有的,不可以直接创建,所以通过静态的简单工厂方法compile(String regex)方法来创建,将给定的正则表达式编译并赋予给Pattern类。

 2、String pattern():返回正则表达式的字符串形式。

其实就是返回Pattern.complile(String regex)的regex参数。示例如下:
String regex ="\?|\*";
Pattern pattern= Pattern.compile(regex);
String patternStr = pattern.pattern();//返回?*

3、Pattern compile(String regex, int flags)

方法功能和compile(String regex)相同,不过增加了flag参数,flag参数用来控制正则表达式的匹配行为,可取值范围如下:

Pattern.CANON_EQ:启用规范等价。当且仅当两个字符的“正规分解(canonicaldecomposition)”都完全相同的情况下,才认定匹配。默认情况下,不考虑“规范相等性(canonical equivalence)”。

Pattern.CASE_INSENSITIVE:启用不区分大小写的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配,要想对Unicode字符进行大小不敏感的匹配,只要将UNICODE_CASE与这个标志合起来就行了。

Pattern.COMMENTS:模式中允许空白和注释。在这种模式下,匹配时会忽略(正则表达式里的)空格字符(不是指表达式里的“\s”,而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。

Pattern.DOTALL:启用dotall模式。在这种模式下,表达式‘.’可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式‘.’不匹配行的结束符。

Pattern.LITERAL:启用模式的字面值解析。

Pattern.MULTILINE:启用多行模式。在这种模式下,‘^’和‘ ’分别匹配一行的开始和结束。此外,‘^’仍然匹配字符串的开始,‘ ’也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。

Pattern.UNICODE_CASE:启用Unicode感知的大小写折叠。在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不敏感的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。

Pattern.UNIX_LINES: 启用Unix行模式。在这个模式下,只有‘\n’才被认作一行的中止,并且与‘.’、‘^’、以及‘$’进行匹配。

 4int flags():返回当前Pattern的匹配flag参数。
5、String[] split(CharSequence input)

Pattern有一个split(CharSequence input)方法,用于分隔字符串,并返回一个String[]。此外String[] split(CharSequence input, int limit)功能和String[]split(CharSequence input)相同,增加参数limit目的在于要指定分割的段数。

  6static boolean matches(String regex, CharSequence input)

是一个静态方法,用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串。方法编译给定的正则表达式并且对输入的字串以该正则表达式为模式开展匹配,该方法只进行一次匹配工作,并不需要生成一个Matcher实例。

  7、Matcher matcher(CharSequence input)

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");
    // 返回p也就是返回该Matcher对象是由哪个Pattern对象的创建的
    m.pattern();

Pattern案例:

 public static void main(String[] args) {

        // 使用Pattern.compile方法编译一个正则表达式,创建一个匹配模式
        Pattern pattern = Pattern.compile("\\?|\\*");

        // pattern()函数返回正则表达式的字符串形式返回\?\*
        String patternStr = pattern.pattern();
        System.out.println(patternStr);// \?|\*

        // flags()返回当前Pattern的匹配flag参数,这里并没有定义
        int flag = pattern.flags();
        System.out.println(flag); // 0


        // split方法对字符串进行分割
        // 123 123 456 456
        String[] splitStrs = pattern.split("123?123*456*456");
        for (int i = 0; i < splitStrs.length; i++) {
            System.out.print(splitStrs[i] + "  ");// 123  123  456  456
        }
        System.out.println("-----------------------------------------");

        // 123 123*456*456
        String[] splitStrs2 = pattern.split("123?123*456*456",2);
        for (int i = 0; i < splitStrs2.length; i++) {
            System.out.print(splitStrs2[i] + "  "); // 123  123*456*456
        }
        System.out.println("-----------------------------------------");

        Pattern p = Pattern.compile("\\d+");
        String[] str = p.split("我的QQ是:456456我的电话是:0532214我的邮箱是:[email protected]");
        for (int i = 0; i < str.length; i++) {
            System.out.printf("str[%d] = %s\n",i, str[i]);
            /** 结果为:
             * str[0] = 我的QQ是:
             * str[1] = 我的电话是:
             * str[2] = 我的邮箱是:[email protected]
             */
        }
        System.out.println("-----------------------------------------");


        // Pattern.matches用给定的模式对字符串进行一次匹配,(需要全匹配时才返回true)
        System.out.println("Pattern.matches(\"\\\\d+\",\"2223\") is " + Pattern.matches("\\d+", "2223"));
        // 返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到
        System.out.println("Pattern.matches(\"\\\\d+\", \"2223aa\")is " + Pattern.matches("\\d+", "2223aa"));
        // 返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到
        System.out.println("Pattern.matches(\"\\\\d+\",\"22bb23\") is " + Pattern.matches("\\d+", "22bb23"));

    }

Matcher类定义

public final class Matcher extends Object implements MatchResult
通过调用模式(Pattern)的matcher方法从模式创建匹配器。创建匹配器后,可以使用它执行三种不同的匹配操作:

    1、matches方法尝试将整个输入序列与该模式匹配。

    2、lookingAt尝试将输入序列从头开始与该模式匹配。

    3、find方法扫描输入序列以查找与该模式匹配的下一个子序列。

每个方法都返回一个表示成功或失败的布尔值。通过查询匹配器的状态可以获取关于成功匹配的更多信息。

匹配器在其输入的子集(称为区域)中查找匹配项。默认情况下,此区域包含全部的匹配器输入。可通过region方法修改区域,通过regionStart和regionEnd方法查询区域。区域边界与某些模式构造交互的方式是可以更改的。

此类还定义使用新字符串替换匹配子序列的方法,需要时,可以从匹配结果计算出新字符串的内容。可以先后使用appendReplacement和appendTail方法将结果收集到现有的字符串缓冲区,或者使用更加便捷的replaceAll方法创建一个可以在其中替换输入序列中每个匹配子序列的字符串。

匹配器的显式状态包括最近成功匹配的开始和结束索引。它还包括模式中每个捕获组捕获的输入子序列的开始和结束索引以及该子序列的总数。出于方便的考虑,还提供了以字符串的形式返回这些已捕获子序列的方法。

匹配器的显式状态最初是未定义的;在成功匹配导致IllegalStateException抛出之前尝试查询其中的任何部分。每个匹配操作都将重新计算匹配器的显式状态。匹配器的隐式状态包括输入字符序列和添加位置,添加位置最初是零,然后由appendReplacement方法更新。

可以通过调用匹配器的reset()方法来显式重置匹配器,如果需要新输入序列,则调用其reset(CharSequence)方法。重置匹配器将放弃其显式状态信息并将添加位置设置为零。

此类的实例用于多个并发线程是不安全的

Matcher类方法详解

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

boolean matches()最常用方法:尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值。
boolean lookingAt() 对前面的字符串进行匹配,只有匹配到的字符串在最前面才会返回true。
boolean find():对字符串进行匹配,匹配到的字符串可以在任何位置。

  2、返回匹配器的显示状态:

int start():返回当前匹配到的字符串在原目标字符串中的位置;
int end():返回当前匹配的字符串的最后一个字符在原目标字符串中的索引位置;
String group():返回匹配到的子字符串。

  3int start()int end()int group()均有一个重载方法

它们分别是int start(int i),int end(int i),int group(int i)专用于分组操作,Mathcer类还有一个groupCount()用于返回有多少组。

4、Matcher类同时提供了四个将匹配子串替换成指定字符串的方法:

1)、String replaceAll(String replacement):将目标字符串里与既有模式相匹配的子串全部替换为指定的字符串。
2)、String replaceFirst(Stringreplacement):将目标字符串里第一个与既有模式相匹配的子串替换为指定的字符串。
3)、还有两个方法Matcher appendReplacement(StringBuffersb, String replacement) 和StringBufferappendTail(StringBuffer sb)也很重要,appendReplacement允许直接将匹配的字符串保存在另一个StringBuffer中并且是渐进式匹配,并不是只匹配一次或匹配全部,而appendTail则是将未匹配到的余下的字符串添加到StringBuffer中。

5、其他一些方法:例如Matcher reset():重设该Matcher对象。

Matcher reset(CharSequence input):重设该Matcher对象并且指定一个新的目标字符串。

Matcher region(int start, int end):设置此匹配器的区域限制。

Matcher案例:

 public static void main(String[] args) {
        Pattern p = Pattern.compile("\\d+");

        // matches()对整个字符串进行匹配
        // 返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功。
        Matcher m = p.matcher("22bb23");
        System.out.println(m.matches());// false
        m = p.matcher("2223");
        // 返回true,因为\d+匹配到了整个字符串
        System.out.println(m.matches());// true

        // lookingAt()对字符串前缀进行匹配
        m = p.matcher("22bb23");
        // 返回true,因为\d+匹配到了前面的22
        System.out.println(m.lookingAt());// true
        m = p.matcher("aa2223");
        // 返回false,因为\d+不能匹配前面的aa
        System.out.println(m.lookingAt());// false

        // find()对字符串进行匹配,匹配到的字符串可以在任何位置。
        m = p.matcher("22bb23");
        System.out.println(m.find()); // true
        m = p.matcher("aa2223");
        System.out.println(m.find()); // true
        m = p.matcher("aabb");
        System.out.println(m.find()); // false
        // 当匹配器匹配失败时,使用返回匹配器状态的方法将出错,例如:m.start();

        m = p.matcher("aa2223bb");
        System.out.println(m.find()); // true

        System.out.println(m.start()); // 2  匹配的开始在位置2的位置
        System.out.println(m.end()); // 6    匹配的结束在原目标字符串中的索引位置
        System.out.println(m.group()); // 2223

        p = Pattern.compile("([a-z]+)(\\d+)");
        m = p.matcher("aaa2223bb");
        // 匹配aaa2223
        m.find();
        // 返回2,因为有2组
        System.out.println(m.groupCount());// 2
        // 返回0, 返回第一组匹配到的子字符串在字符串中的索引号
        System.out.println(m.start(1));// 0
        // 返回3
        System.out.println(m.start(2)); // 3
        // 返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置.
        System.out.println(m.end(1));// 3
        // 返回2223,返回第二组匹配到的子字符串
        System.out.println(m.group(2)); //2223

        
        testGroup();
    }

    static void testGroup() {
        Pattern p;
        Matcher m;
        // 正则表达式中用小括号 () 来做分组,也就是括号中的内容作为一个整体,所以m.groupCount()值为2
        p = Pattern.compile("([a-z]+)(\\d+)");
        m = p.matcher("aaa2223bb");

        while (m.find()){
            System.out.println(m.group()); //aaa2223

            System.out.println(m.groupCount());// 2

            System.out.println(m.group(1)); // aaa
            System.out.println(m.start(1));// 0
            System.out.println(m.end(1));// 3

            System.out.println(m.group(2)); // 2223
            System.out.println(m.start(2)); // 3
            System.out.println(m.end(2)); // 7

            System.out.println("-------------------");
            System.out.println(m.group(3)); // java.lang.IndexOutOfBoundsException: No group 3
        }
    }

附上2个简单的Demo,有利于对上面的api的了解:

 public static void main(String[] args) throws Exception {

        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            String input = sc.nextLine();
            System.out.println("input is : " + input);

            // 检测输入的EMAIL地址是否以非法符号"."或"@"作为起始字符
            Pattern p = Pattern.compile("^@");
            Matcher m = p.matcher(input);
            if (m.lookingAt()) {
                System.out.println("EMAIL地址不能以'@'作为起始字符");
            }

            // 检测是否以"www."为起始
            p = Pattern.compile("^www.");
            m = p.matcher(input);
            if (m.lookingAt()) {
                System.out.println("EMAIL地址不能以'www.'起始");
            }

            // 检测是否包含非法字符
            p = Pattern.compile("[^A-Za-z0-9.@_-~#]+");
            m = p.matcher(input);
            StringBuffer sb = new StringBuffer();
            boolean result = m.find();
            System.out.println("result is : " + result);

            boolean deletedIllegalChars= false;

            while (result) {
                // 如果找到了非法字符那么就设下标记
                deletedIllegalChars= true;
                // 如果里面包含非法字符如冒号双引号等,那么就把他们消去,加到SB里面
                m.appendReplacement(sb, "");
                result = m.find();
            }

            // 此方法从添加位置开始从输入序列读取字符,并将其添加到给定字符串缓冲区。
            // 可以在一次或多次调用 appendReplacement 方法后调用它来复制剩余的输入序列。
            m.appendTail(sb);
            if (deletedIllegalChars){
                System.out.println("输入的EMAIL地址里包含有冒号、逗号等非法字符,请修改");
                System.out.println("您现在的输入为: " + input);
                System.out.println("修改后合法的地址应类似: " + sb.toString());
            }
        }

        sc.close();
    }
    /**  博客里面的第四点的api
     * 4、Matcher类同时提供了四个将匹配子串替换成指定字符串的方法:
     *
     *         1)、String replaceAll(Stringreplacement):将目标字符串里与既有模式相匹配的子串全部替换为指定的字符串。
     *         2)、String replaceFirst(Stringreplacement):将目标字符串里第一个与既有模式相匹配的子串替换为指定的字符串。
     *         3)、还有两个方法Matcher appendReplacement(StringBuffer sb, String replacement)
     *             和StringBuffer appendTail(StringBuffer sb)也很重要,
     *             appendReplacement允许直接将匹配的字符串保存在另一个StringBuffer中并且是渐进式匹配,
     *             并不是只匹配一次或匹配全部,而appendTail则是将未匹配到的余下的字符串添加到StringBuffer中。
     */

/**
 * @program: my-demo-study
 * @Date: 2019/10/20 14:00
 * @Author: taoyuliang
 * @Description:
 * 判断身份证:要么是15位,要么是18位,最后一位可以为字母,并写程序提出其中的年月日。
 * 可以使用正则表达式来定义复杂的字符串格式:(\d{17}[0-9a-zA-Z]|\d{14}[0-9a-zA-Z])可以用来判断是否为合法的15位或18位身份证号码。
 * 因为15位和18位的身份证号码都是从7位到第12位为身份证为日期类型。这样我们可以设计出更精确的正则模式,提取身份证号中的日期信息。
 */
public class classIdentityMatch {
    public static void main(String[] args) {
        // 测试是否为合法的身份证号码
        String[]id_cards = { "130681198712092019","13068119871209201x","13068119871209201","123456789012345",
                "12345678901234x","1234567890123"};

        // 测试是否为合法身份证的正则表达式
        Pattern pattern = Pattern.compile("(\\d{17}[0-9a-zA-Z]|\\d{14}[0-9a-zA-Z])");

        // 用于提取出生日字符串的正则表达式
        Pattern pattern1 = Pattern.compile("\\d{6}(\\d{8}).*");
        // 用于将生日字符串分解为年月日的正则表达式
        Pattern pattern2 = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})");

        Matcher matcher = pattern.matcher("");
        for (int i = 0; i < id_cards.length; i++) {
            matcher.reset(id_cards[i]);
            System.out.println(id_cards[i] + " is id cards:" + matcher.matches());

            // 如果它是一个合法的身份证号,提取出出生的年月日
            if (matcher.matches()) {
                Matcher matcher1 = pattern1.matcher(id_cards[i]);
                matcher1.lookingAt();
                String birthday = matcher1.group(1);
                System.out.println("birthday is " + birthday);

                Matcher matcher2 = pattern2.matcher(birthday);
                if (matcher2.find()) {
                    System.out.println("它对应的出生年月日为:" + matcher2.group(1) + "年" + matcher2.group(2) + "月"
                            +matcher2.group(3) + "日");
                }

            }

            System.out.println();
        }
    }
    /** 博客里面的第五点的api
     * 5、其他一些方法:例如Matcherreset():重设该Matcher对象。
     *
     *         Matcher reset(CharSequence input):重设该Matcher对象并且指定一个新的目标字符串。
     *
     *         Matcher region(int start, int end):设置此匹配器的区域限制。
     */
}

参考:
不会正则表达式?看这篇就够了
Java Pattern和Matcher字符匹配详解
java Pattern和Matcher详解
java Pattern和Matcher详解

猜你喜欢

转载自blog.csdn.net/weixin_43719015/article/details/102646625
今日推荐