正则表达式知识点扫盲

Lou.Chen2022年7月8日
大约 21 分钟

1.正则表达式

1.1 概念

正则表达式定义了字符串的模式。

正则表达式可以用来搜索、编辑或处理文本。

正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。

1.2 常见正则特殊字符含义

若不加任意数量限定符,则默认限定匹配数量为1

  • 表示匹配一个普通的反斜杠:\\

  • 匹配一个数字正则:\\d ,在Java中使用\取消对\的转义,表示\d在正则中的意思为数字

    • System.out.print("\\");    // 输出为 \
      System.out.print("\\\\");  // 输出为 \\
      
字符含义示例
.表示除\n之外的任意字符abc.edf 匹配的字符串abcedf之间只能有一个任意字符。以可以匹配中文字符
.*表示可以匹配任意多个字符,这里的*表示任意数量abc.* 匹配的字符串abc后可以跟任意多个字符
^表示以匹配的数据必须以指定字符开头^abc 匹配的字符串必须以abc开头, 例如: abc123,可以匹配。 123abc不能匹配
$表示以匹配的数据必须以指定字符结尾abc^ 匹配的字符串必须以abc结尾,例如:abc123 不能匹配。123abc可以匹配
[]表示只有中括号中的字符才能匹配,只能匹配单个字符a[1234]c 匹配的字符串只可以为:a1c ,a2c ,a3c, a4c
[^abc]表示不匹配中括号的任意一个字符a[^123]c 不匹配的字符串 a1c, a2c, a3c
[a-z]表示匹配a到z到任意一个字符
[^a-z]表示不匹配a到z到任意一个字符
``表示,只能匹配其中一项
\.匹配.这个字符因为.在正则中有特殊含义,即加\取消转义
\n换行(LF),将当前位置移到下一行开头Unix系统里,每行结尾只有\n

windows中,换到当前位置的下一行,而不会回到行首
\r回车(CR) ,将当前位置移到本行开头windows中,回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖
\r\n回车并换行Windows系统里面,每行结尾是\r\n

Linux中遇到换行符("\n")会进行回车+换行的操作,回车符\r反而只会作为控制字符("^M")显示,不发生回车的操作。
windows中要回车符+换行符("\r\n")才会回车+换行,缺少一个控制符或者顺序不对都不能正确的另起一行。
\t水平制表(HT)(跳到下一个TAB位置)
\d匹配数字,等同 [0-9]
\D匹配非数字,等效于 [^0-9]
\\(匹配( 普通文本,其它特殊字符匹配类似小括号在正则中特殊含义,即使用\取消小括号的本身含义,而\在正则中也有特殊含义,即再使用\取消本身的含义
\b匹配一个边界字符,即\b后面无字符或者空格或者回车或者标点(逗号,感叹号等)正则:llo\\b 查找字符串hello \n \n123 hello2 12hello 匹配第一个hello和最后一个12hello
\B匹配一个非边界字符
\s匹配任何空白字符,包括空格、制表符、换页符等。[\f\n\r\t\v] 等效。
\S匹配任何非空白字符。与 [^\f\n\r\t\v] 等效。
\w匹配任何字类字符,包括下划线。[A-Za-z0-9_]等效。
\W与任何非单词字符匹配。[^A-Za-z0-9_]等效。
[\u4E00-\u9FFF]匹配单个中文
1.2.1 集合
1.2.1.1 交集
  • [[a-z]&&[^aeiou]] 从 a 到 z 的所有字符(也就是小写英文字符)”与“除 a、e、i、o、u 之外的任何字符”取交集,也就是所有的小写辅音字母

    "a".matches("[[a-z]&&[^aeiou]]"); // => False
    "b".matches("[[a-z]&&[^aeiou]]"); // => True
    
  • [\\w&&[^_]] 英文大小写字母和数字(不包括下画线)

    "a".matches("[\\w&&[^_]]"); // => True
    "0".matches("[\\w&&[^_]]"); // => True
    "_".matches("[\\w&&[^_]]"); // => False
    
1.2.1.2 并集
  • [[0-4]||[6-9]]等价于[[0-4][6-9]]等价于[0-46-9]

    因为“并”是默认的运算,如果字符组内部只出现一个字符组,可以理解为“单个字符组与空集取并集”,换句话说, [[0-4]]等价于[0-4]

1.2.2 单词边界
  • \b 所识别的单词字符,可以是各语言中的单词字符(包括中文字符)但不包括空白和标点(包括中文的空白和标点);而且,单词字符必须出现。

            String reg = "ab\\b";
            System.out.println(Pattern.compile(reg).matcher("aabb asda ab!c").find()); //true
            System.out.println(Pattern.compile(reg).matcher("aabb asda ab,").find()); //true
            System.out.println(Pattern.compile(reg).matcher("aabb asda ab,").find()); //true
            System.out.println(Pattern.compile(reg).matcher("aabb asda ab!").find()); //true
            System.out.println(Pattern.compile(reg).matcher("aabb asda ab哈哈").find()); //false
    
1.2.3 环视

https://mp.weixin.qq.com/s/VqiZ6fVL20sJ8NUoQJjm2gopen in new window

环视顾名思义就是**「环顾四周,向左看看右看看,找一个合适的位置」。环视匹配的是一个『位置』或者『范围位置』而不是字符,这点尤为重要。**

表达式说明
(?=Expression)顺序肯定环视,表示所在的位置的右侧能匹配Expression
(?!Expression)顺序否定环视,表示所在的位置右侧不能匹配Expression
(?<=Expression)逆序肯定环视,表示所在的位置左侧能匹配Expression
(?<!Expression)逆序否定环视,表示所在的位置左侧不能匹配Expression

⚠️逆序环视中使用的子表达式必须有长度上限

因为正则表达式的匹配过程是从左到右的,所以如果我们要**「判断一个位置的右边满不满足某个条件,这就叫顺序环视」。如果我们「判断一个位置的左边满不满足某种条件的话,这就叫做逆序环视。」**

?表示疑问,<表示方向是当前位置的左边,=表示满足匹配。!表示不被满足匹配。

实际应用:

  • 数字千分位的处理

    123456789 划分为千分位,即 123,456,789

    从后往前数,找出每三个数字替换逗号的位置,并且逗号前面必须至少有一个数字

    (?<=\d)(\\d{3})+$

            String reg="123456789";
            Matcher matcher = Pattern.compile("(?<=\\d)(?=(\\d{3})+$)").matcher(reg);
    				//将所有匹配的位置替换为逗号
    				//123,456,789
            System.out.println(matcher.replaceAll(","));
    
    
  • 获取匹配位置范围的值

            String s = "xxx12312Ether Gather0/1/2/3 port up down.23sad";
            Matcher matcher = Pattern.compile("(?<=Ether).*(?=port up down)").matcher(s);
            while (matcher.find()) {
                // Gather0/1/2/3
                System.out.println(matcher.group());
                //13
                System.out.println(matcher.start());
                //28
                System.out.println(matcher.end());
            }
    
1.2.4 \数字 获取分组内容

在正则表达式中,可以使用\数字表示某个分组,即分组\数字是同时存在的

例如正则:(\d)+(.{3}).*\1

  • 使用\1代表分组 (\d)
  • 使用\2代表分组(.{3})

eg:判断是否匹配三个连续字符至少重复出现两次

        String s = "111abc112223221abc112";
        String reg=".*(.{3})(.*\\1.*)+";
        Matcher matcher = Pattern.compile(reg).matcher(s);
				//true
        System.out.println(matcher.matches());

1.3 数量表示

  • 若存在分组,则规定数量 (\\d{2,3}) 或者 (\\d){2,3}

    • 例如匹配:aa 2,3123,12184 bb 数字部分

      正则:(\\d+,)*\\d+

代码/语法说明
*重复零次或更多次
+至少重复1次
?重复零次或一次
{n}重复n次
{n,}至少重复n次
{n,m}重复n到m次
1.3.1 匹配优先

https://mp.weixin.qq.com/s/AxTuXvKINhX6qLzj6Q9UJwopen in new window

匹配优先又叫做**「贪婪匹配」,它的特点是,「在量词作用下的表达式会尽可能尝试匹配满足条件的字符,直到后面的字符串不满足这个表达式或者到达字符串的结尾」**。只

有在正则表达式整体在尝试匹配却没有匹配成功的情况下,匹配优先的这部分才会**「归还已经匹配的部分字符,好让整体能够匹配成功。」**

        String s = "adc123eee789kkk123";
        Matcher matcher = Pattern.compile(".*(\\d+)").matcher(s);
        while (matcher.find()) {
            //3
            System.out.println(matcher.group(1));
        }

分析

  • 首先 正则表达式的.*是一个整体,表示.可以匹配无数次,或者0次。.是一个元字符可以匹配任何字符(除了行的结束符)所以在遇到行结束符之前,这部分表达式对于接下来的字符都是可以匹配成功的,就像上图表示的那样,一直匹配到这一行的结束。
  • 「为了正则表达式的整体能够匹配成功,.*需要归还已经匹配到的到最后一个字符」,即将最后一个字符3让出来给后面到正则\d+尝试匹配,即匹配成功。至此,整个匹配过程完成
  • .*在匹配的过程中让出了一部分已经匹配的字符;这个过程我们称之为**「回溯」**
1.3.2 忽略优先

忽略优先又叫做**「懒惰匹配」,它的特点是,「在量词作用下的表达式会尽可能忽略掉满足条件的字符」**,只有在正则表达式整体在尝试匹配却没有匹配成功的情况下,忽略

优先的这部分才会去**「匹配满足条件的那些字符,好让整体能够匹配成功。」**

「量词默认是匹配优先(贪婪匹配)的」,如果在上面量词(需要是能够表示范围的量词)的后面再添加一个?,那么上面的量词就变成了忽略优先的量词,比如??*?+?

        String s = "adc1234eeeas7890kkkkk1123";
        Matcher matcher = Pattern.compile(".*?(\\d+)").matcher(s);
        while (matcher.find()) {
            //1234
            //7890
            //1123
            System.out.println(matcher.group(1));
        }

分析

  • 正则表达式的.*?是一个整体,表示.可以匹配0次或者无数次,「但是尽量不匹配」。于是,刚开始,.*?就什么也没有匹配」
  • 接着使用\d+去匹配a发现不匹配,即将a再交给.*匹配,即匹配成功。接着使用\d+去匹配d,匹配失败,交给.*匹配成功。重复该步骤,一直到\d+匹配成功为止。
  • 即依次匹配到 1234、7890、1123
多个惰性匹配
        String s = "adc123eas78kk";
        Matcher matcher = Pattern.compile("(\\d+?)(.*?)").matcher(s);
        while (matcher.find()) {
            //1 空串
            //2 空串
            //3 空串
            //7 空串
            //8 空串
            System.out.println(matcher.group(1)+" "+matcher.group(2));
        }
        String s = "adc123eas78kk";
        Matcher matcher = Pattern.compile("(\\d+?)(.+?)").matcher(s);
        while (matcher.find()) {
            //1 2
            //3 e
            //7 8
            System.out.println(matcher.group(1)+" "+matcher.group(2));
        }
        String s = "adc123eas78kk";
        Matcher matcher = Pattern.compile("(\\d*?)(.+?)").matcher(s);
        while (matcher.find()) {
            //空串 a 
            //空串 d
            //空串 c
            //空串 1
            //空串 2
						//.....
            System.out.println(matcher.group(1)+" "+matcher.group(2));
        }
    }
        String s = "adc123eas78kk";
        Matcher matcher = Pattern.compile("(\\d*?)(.*?)").matcher(s);
        while (matcher.find()) {
            //空串 空串 
            //空串 空串
            //空串 空串
						//.....
            System.out.println(matcher.group(1)+" "+matcher.group(2));
        }
匹配css标签
        String s = "<a>link</a> <span>hello</span><span>world</span> <button>click</button>";
        Matcher matcher = Pattern.compile("<(\\w+)>.*?</\\1>").matcher(s);
        while (matcher.find()) {
            //<a>link</a>
            //<span>hello</span>
            //<span>world</span>
            //<button>click</button>
            System.out.println(matcher.group());
        }

\w匹配任何字类字符,包括下划线

\1 匹配第一个分组\w+,保证开始和结束为同一标签

.*?惰性匹配

匹配字符串中带有¥前缀的格式化数字
        String s = "aaa¥100aaa\n" +
                "aaa¥a100aaa\n" +
                "aaa¥1a00aaa\n" +
                "aaa¥100.00aaa\n" +
                "aaa¥100,000,000.00aaa";
        Matcher matcher = Pattern.compile("¥\\d+(,\\d{3})*(\\.\\d+)?").matcher(s);
        while (matcher.find()) {
            //¥100
            //¥1
            //¥10.0
            //¥100,000,000.00
            System.out.println(matcher.group());
        }

不需要使用惰性匹配

  • 注意逗号之后必须要有三个数字
1.3.3 占有匹配

https://mp.weixin.qq.com/s/ezul6Dg4f6_WLHXAkEgd6gopen in new window

首先,占有优先的匹配方式跟贪婪匹配的方式很像。它们之间的区别就是**「占有优先的匹配方式,不会归还已经匹配的字符」,这点很重要。这也是为什么「占有优先的匹配**

方式效率比较高的原因。因为它作用的表达式在匹配结束之后,不会保留备用的状态,也就是不会进行回溯」。但是,贪婪匹配会保留备用的状态,如果之前的匹配没有成功

的话,它会回退到最近一次的保留状态,也就是进行回溯,「以便正则表达式整体能够匹配成功」。那么占有优先的表示方式是怎样的?「占有优先匹配就是在原来的量词的

后面添加一个+」,像下面展示的这样。

.?+
.*+
.++
.{3, 6}+
.{3,}+

因为正则表达式有很多流派,有一些流派是不支持占有优先这种匹配方式的,比如**「JavaScript」**就不支持这种匹配的方式

但是我们可以使用**「肯定的顺序环视」**来模拟占有优先的匹配。

匹配以数字9结尾的字符串

贪婪匹配:

        String s = "119 1219 339";
        Matcher matcher = Pattern.compile("\\d*9").matcher(s);
        while (matcher.find()) {
            //119
            //1219
            //339
            System.out.println(matcher.group());
        }

「占有匹配」:

    String s = "119 1219 339";
     Matcher matcher = Pattern.compile("\\d*+9").matcher(s);
        while (matcher.find()) {
          	//不能匹配任何字符,因为\d已经匹配了所有数字,即9匹配失败
            System.out.println(matcher.group());
        }
  • 分析:

    正则表达式是从左向右匹配的,对于\d*+这个整体,我们在进行匹配的时候可以先把\d*+看作是\d*进行匹配。对于\d*+这部分表达式来说它在开始匹配的时候会匹配尽可能多的数字,「对于我们给出的测试用例,\d*+都是可以匹配的,所以\d*+直接匹配到了每一行数字的结尾处」。然后因为\d*+是一个整体,表示占有优先的匹配。所以**「当\d*+匹配完成之后,这个整体便不再归还已经匹配的字符了」**。但是我们正则表达式的后面还需要匹配一个字符9,但是前面已经匹配到字符串的结尾了,再没有字符给9去匹配,所以上面的测试用例都匹配失败了。

在开始匹配的过程中**「我们可以把占有优先当做贪婪匹配来进行匹配,但是一旦匹配完成就跟贪婪匹配不一样了,因为它不再归还匹配到的字符」。所以对于占有优先的匹配方式,我们只需要牢记「占有优先匹配方式匹配到的字符不再归还」**就可以了。

1.3.4 固化分组

跟占有优先匹配作用一样的**「固化分组」** 写法为:(?>表达式)

对于不支持固化分组的流派来说,如果这些流派支持**「肯定的顺序环视」「捕获的括号」**的话,我们可以使用肯定的顺序环视来模拟固化分组。

为什么肯定的顺序环视可以模拟固化分组呢?

固化分组的特性就是匹配完成之后,丢弃了固化分组内表达式的备用状态,然后不会进行回溯。又因为**「环视一旦匹配成功之后也是不会进行回溯的」**,所以我们可以利用肯定的顺序环视来模拟固化分组。

为什么可以使用(?=(表达式))\1这个表达式来模拟固化分组(?>表达式)

解释:首先是一个肯定的顺序环视,需要在当前位置的后面找到满足表达式的匹配,如果找到的话,接下来\1会匹配环视中已经匹配到的那部分字符。因为在顺序环视中的正则表达式不会受到顺序环视后面表达式的影响,所以顺序环视内部的表达式在匹配完成之后不会进行回溯。然后后面的\1再次匹配环视里面表达式匹配到的内容,这样就模拟了一个固化分组。

  • 固化分组
        String s = "119 1219 339";
        Matcher matcher = Pattern.compile("(?>\\d*)9").matcher(s);
        while (matcher.find()) {
          	//不能匹配任何字符,因为\d已经匹配了所有数字,即9匹配失败
            System.out.println(matcher.group());
        }
  • 模拟的固化分组
        String s = "119 1219 339";
        Matcher matcher = Pattern.compile("(?=(\\d*))\\19").matcher(s);
        while (matcher.find()) {
            //不能匹配任何字符,因为\d已经匹配了所有数字,即9匹配失败
            System.out.println(matcher.group());
        }

「模拟的固化分组在效率上要比真正的固化分组慢一些」,因为\1的匹配也是需要花费时间的。不过对于贪婪匹配所造成的的回溯来说,这点匹配的时间一般还是很短的。

2.Java中正则的使用

2.1 主要类

java.util.regex 包主要包括以下三个类:

  • Pattern 类:

    pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

  • Matcher 类:

    Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

  • PatternSyntaxException

    PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

2.2 捕获组

捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。

例如,正则表达式 (dog) 创建了单一分组,组里包含"d","o",和"g"。

捕获组是通过从左至右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:

  • ((A)(B(C)))
  • (A)
  • (B(C))
  • (C)

可以通过调用 matcher 对象的 groupCount 方法来查看表达式有多少个分组。groupCount 方法返回一个 int 值,表示matcher对象当前有多个捕获组。

还有一个特殊的组(group(0)),它总是代表整个表达式。该组不包括在 groupCount 的返回值中。

  • 如果没有分组,即groupCount0,则总是可以通过group(0),返回查询的整体结果

  • 如果分组为1,若使用(ABC)包含整个正则,即groupCount1,则使用group(0)group(1)返回的结果相同

    ​ 若使用ABC(DEF),即group(0)代表查询的整个表达式内容,使用group(1)获取第一部分内容

  • 如果分组为2,即(ABC)(DEF),即groupCount2, 即group(0)代表查询的整个表达式内容,则使用group(1)获取第一部分分组内容,group(2)获取第二部分分组内容

2.3 Pattern类

2.3.1 **内联匹配模式 **
2.3.1.0 不使用匹配模式
//匹配一个任意数字,至少出现一次
Pattern pattern = Pattern.compile("\\d+");
2.3.1.1 忽略大小写
  • Pattern.CASE_INSENSITIVE 或者 正则中使用(?i)
    • (?i)所在位置右侧的表达式开启忽略大小写模式
				//匹配java字符串,并忽略大小写
				//Pattern pattern = Pattern.compile("java",Pattern.CASE_INSENSITIVE);
        //匹配所有字母并忽略大小写
				String s = "abc123456 \ndef789\n1 HIG";
        Matcher matcher = Pattern.compile("(?i)[a-z]+").matcher(s);
        while (matcher.find()) {
          	//abc
            //def
            //HIG
            System.out.println(matcher.group());
        }
2.3.1.2 多行模式
  • Pattern.MULTILINE 或者 正则中使用(?m)

    • (?m) 表示所在位置右侧的表示式开启指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。

      注意:(?m)只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。

        String s = "123abc123456\ndef789\n1 HIG";
				//匹配多行文本中以字母开头和数字结尾的数据s
        Matcher matcher = Pattern.compile("(?m)^[a-z]+\\d+$").matcher(s);
        while (matcher.find()) {
            //def789
            System.out.println(matcher.group());
        }
2.3.1.3 单行模式
  • Pattern.DOTALL 或者 正则中使用(?s)

    • (?s) 表示所在位置右侧的表示式开启指定单行模式。更改句点字符 . 的含义,将匹配所有字符(包括\n

      注意:(?s)通常在匹配有换行的文本时使用

        String s = "123abc\r\n123456 \n def789 \n 1 HIG";
				//匹配字母开头和至少一字母之间的所有字符
        Matcher matcher = Pattern.compile("(?s)[a-z]+(.*)\\d+").matcher(s);
        if (matcher.find()) {
          	//分组长度为1
            //\r\n123456 \n def789 \n 
            System.out.println(matcher.group(1));
        }
2.3.1.4 组合使用内联模式

(?is)(?im)

2.3.2 **split 根据正则分隔 **
  • String[] split(CharSequence input) 返回的数组会去掉结尾的所有空串

    • input:要查找的字符串
  • String[] split(CharSequence input, int limit)

    • input:要查找的字符串
    • limit:返回元素的个数
      • 若limit为负数或者大于字符串返回的最大长度,则返回的数量不受限制,但返回的结尾空串不会去掉
      • 若limit为0,则和不带此参数的用法一致,返回的数组会去掉结尾的所有空串
        Pattern p1 = Pattern.compile("java");
        String[] split = p1.split("123java456java789");
        // [123, 456, 789] 长度为3
        System.out.println(Arrays.toString(split));

        Pattern p1 = Pattern.compile("java");
				//limit为负数,或者大于返回的最大长度
        String[] split = p1.split("123javajava456java789java",-1);
        // [123, , 456, 789, ] 长度为5
        System.out.println(Arrays.toString(split));
2.3.3 matches 是否完全匹配
  • boolean matches(String regex, CharSequence input) 查找的每个字符是否符合正则要求,即完全匹配
    • regex:正则
    • input:要查找的字符
        //true
        System.out.println(Pattern.matches("\\d+", "2"));
        //false
        System.out.println(Pattern.matches("\\d*", "001abc"));
        //true
        System.out.println(Pattern.matches("\\d*", "001"));
        //false
        System.out.println(Pattern.matches("\\d+", ""));
        //false
        System.out.println(Pattern.matches("java", "javahello"));
        //true
        System.out.println(Pattern.matches("java", "java"));

2.4 Matcher类

2.4.1 根据Pattern获取Mathcer对象
				Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher("01234hello world!");
2.4.2 是否匹配

⚠️注意,以下匹配查找方法会改变查找到位置索引,即执行一次,查找到起始索引改变一次。 若想要再次从头查找,则需要调用reset方法,重置查找位置

  • boolean matches() 是否完全匹配

  • boolean lookingAt() 从头开始是否匹配

    • 若没有匹配到,则不会改变索引位置
  • boolean find() 从任意位置是否匹配到

    boolean find(int start) 从指定起始位置是否匹配到,此方法始终会执行reset()操作

        Pattern p2 = Pattern.compile("\\d+");
        String s1 = "123java456";
        Matcher matcher = p2.matcher(s1);
        //true
        System.out.println(matcher.lookingAt());
        //0
        System.out.println(matcher.start());
        //true
        System.out.println(matcher.find());
        //7
        System.out.println(matcher.start());
2.4.3 重置操作
  • Matcher reset() 重置查找的位置
  • Matcher reset(CharSequence input) 重置查找到位置,并重置查找到字符串
        Pattern p2 = Pattern.compile("\\d+");
        String s1 = "123java456";
        Matcher matcher = p2.matcher(s1);
        //true
        System.out.println(matcher.lookingAt());
        //0
        System.out.println(matcher.start());
        //重置位置操作
        matcher.reset();
        //true
        System.out.println(matcher.find());
        //0
        System.out.println(matcher.start());
2.4.4 正则分组

调用分组相关方法之前,需要首先执行查找操作,即find()方法,此方法会在对象内部自动为需要的分组数据设置相关值

  • int groupCount() 获取分组个数

    • 若没有中括号,则分组个数位0,但可以通过group(0)获取分组内容
  • int start() 获取第一个分组的起始匹配位置

    int start(int group) 获取指定分组的起始位置

  • int end() 获取第一个分组的终止匹配位置

    int end(int group) 获取指定分组的终止匹配位置

  • String group() 获取第一个分组内容

    • group(0)

    String group(int group) 获取指定分组的内容

正则:

  • ([0-2]{1,3})([a-c]{1,3}) 2个分组,通过group(1)获取第一部分内容,group(2)获取第二部分内容,group(0)是查询的整个表达式的内容,无分组概念
  • ([0-2]{1,3}[a-c]{1,3}) 1个分组,通过 group(1)或者group(0)获取第一部分内容
  • [0-2]{1,3}([a-c]{1,3}) 1个分组,通过 group(1)获取第一部分内容,group(0)是查询的整个表达式的内容,无分组概念
  • [0-2]{1,3}[a-c]{1,3} 0个分组, 通过group(0)获取匹配到的内容
  • 获取所有数字后的字母
        Pattern p4 = Pattern.compile("([0-2]{1,3})([a-c]{1,3})");
        Matcher m2 = p4.matcher("012abc#11@12ac#22@1b#333@23aa");
        //分组个数 2
        System.out.println(m2.groupCount());
        List<String> list=new ArrayList<>();
        while (m2.find()) {
            //获取所有第二个分组中的内容
            list.add(m2.group(2));
        }
        //[abc, ac, b]
        System.out.println(list);
2.4.5 设置和获取查找范围

Matcher region(int start, int end) 设置查找范围

int regionStart() 获取region设置的查找起始位置

int regionEnd() 获取region设置的查询终止位置

2.4.6 查找替换

String replaceAll(String replacement) 替换所有匹配到的字符串

String replaceFirst(String replacement) 替换匹配到的第一次字符串

        Pattern p7 = Pattern.compile("\\d+");
        Matcher matcher1 = p7.matcher("123abc456def789hig");
        //替换所有匹配到的字符串
        //@abc@def@hig
        System.out.println(matcher1.replaceAll("@"));
        //替换匹配到的第一次字符串
        //#abc456def789hig
        System.out.println(matcher1.replaceFirst("#"));
2.4.7 分段替换追加
  • Matcher appendReplacement(StringBuffer sb, String replacement)

    • 分段匹配,将每次find()查找的结果到替换为指定字符,然后将该匹配位置之前的所有数据添加到StringBuffer对象中

          Pattern p8 = Pattern.compile("java");
          Matcher m4 = p8.matcher("java123hellojavaworldjava");
          StringBuffer sb = new StringBuffer();
          while (m4.find()) {
              m4.appendReplacement(sb, "#");
              //#
              //#123hello#
              //#123hello#world#
              System.out.println(sb);
          }
      }
      
  • StringBuffer appendTail(StringBuffer sb)appendReplacement执行后,未匹配到的剩余全部部分加到StringBuffer对象中

    • 必须和appendReplacement配合使用,否则返回所有匹配到的整个字符串

    •     public static void main(String[] args) {
              Pattern p8 = Pattern.compile("\\d+");
              Matcher m4 = p8.matcher("123abc456def789ghi");
              StringBuffer sb = new StringBuffer();
              StringBuffer sb1 = new StringBuffer();
              while (m4.find()) {
                  m4.appendReplacement(sb, "#");
                  m4.appendTail(sb1);
                  //abc456def789ghi
                  //abc456def789ghidef789ghi
                  //abc456def789ghidef789ghighi
                  System.out.println(sb1);
              }
          }