JAVA基础之正则表达式

1、表达式基础

1.1 句点符号

假设在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束。要构造出这个正则表达式,可以使用一个通配符——句点符号“.”。这样,完整的表达式就是“t.n”,它匹配“tan”、“ten”、“tin”和“ton”,还匹配“t#n”、“tpn”甚至“t n”,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符。

1.2 方括号符号

为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号(“[]”)里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为在方括号之内只能匹配单个字符。

此外,方括号内还可以用“-”表示跨度,如“[a-g]”匹配字母表中a到g的小写字母,“[1-9]”匹配1到9的数字。

可以使用“&&”取交集,比如“[1-9&&[^456]]”,匹配1到9中除了4、5、6以外的数字。

1.3 “或”符号

如果除了上面匹配的所有单词之外,还想要匹配“toon”,那么,你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”,使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号,因为方括号只允许匹配单个字符;这里必须使用圆括号“()”。圆括号还可以用来分组。

1.4 表示匹配次数的符号

符号 意义
? 0次或1次
+ 1次或多次,即至少1次
* 0次或多次,即任意多次
{n} 恰好n次
{n,m} n次到m次

假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如下图所示。

1.png

在正则表达式中,连字符(“-”)有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“\”。

假设进行搜索的时候希望连字符号可以出现,也可以不出现——即,999-99-9999999999999都属于正确的格式。这时,可以在连字符号后面加上“?”数量限定符号,如下图所示:

2.png

1.5 “否”符号

”符号称为“否”符号。如果用在方括号内,“”表示不想要匹配的字符。例如,下图的正则表达式匹配所有单词,但以“X”字母开头的单词除外。

3.png

1.6 圆括号

1.6.1 限定量词作用的范围


(cat)? 匹配0个或1个“cat”
(cat)+ 匹配1个或多个“cat”

1.6.2 限定多选结构的范围


(cat|dog) 匹配“cat”或“dog”

1.6.3 分组捕获

位于圆括号之间的模式匹配的内容都会被捕获,当模式中有嵌套的圆括号时,变量的编号会按照圆开括号出现的位置一次进行。


(A-Za-z)((-)\d{2})匹配”A22-33”时,匹配情况如下:

第一个出现的圆括号中的正则表达式为:A-Za-z
匹配结果Group 1: A22

第二个出现的圆括号中的正则表达式为:\d{2}
匹配结果Group 2: 22

第三个出现的圆括号中的正则表达式为:(-)\d{2}
匹配结果Group 3: -33

第四个出现的圆括号中的正则表达式为:-
匹配结果Group 4: -

1.6.4 分组不捕获

当圆括号与“?:”组合使用时,表示非捕获型分组。圆括号的内容不作为捕获对象,当圆括号中的内容不是想捕获的对象时,采用非捕获圆括号可以提高匹配效率。


(\w(?:\d{2}))((?:-)\d{2})匹配” A22-33”情况如下:
Group 1: A22
Group 2: -33
注:\d{2}匹配的”22”没有被捕获,-匹配的“-”也没有被捕获。

1.6.5 反向引用捕获文本


([ab])\1,圆括号内的“[ab]” 可以匹配“a”或“b”,后面的“\1”代表,如果前面匹配的文本引用。如果捕获组匹配到“a”,那么反向引用也就只能匹配“a”,同理,如果捕获组匹配到的是“b”,那么反向引用也就只能匹配“b”。由于后面反向引用“\1”的限制,要求必须是两个相同的字符,在这里也就是“aa”或者“bb”才能匹配成功。

1.6.6 前瞻

当圆括号与“?=”组合使用时,表示前瞻,代表该表达式会作为匹配校验,但不会出现在匹配结果字符串里面。


(John) (?=Resig)匹配情况如下:
John:不匹配,因为后面没有跟着Resig
John Backus:不匹配,后面跟着的不是Resig
John Reisg:匹配,John后面跟着Resig。但是匹配的结果是“John”而不是“John Reisg”。

1.7 预定义字符

符号 意义
\d 数字字符:[0-9]
\D 非数字字符
\s 空白字符:[\t\n\x0B\f\r]
\S 非空白字符
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符

1.8 边界匹配字符

符号 意义
^ 行首
$ 行尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾

1.9 匹配模式

正则表达式有三种匹配模式:

  • 贪婪(greedy)
  • 勉强(reluctant)
  • 侵占(possessive)
贪婪 勉强 侵占 意义
X? X?? X?+ 匹配 X 零次或一次
X* X*? X*+ 匹配 X 零次或多次
X+ X+? X++ 匹配 X 一次或多次
X{n} X{n}? X{n}+ 匹配 X n 次
X{n,} X{n,}? X{n,}+ 匹配 X 至少 n 次
X{n,m} X{n,m}? X{n,m}+ 匹配 X 至少 n 次,但不多于 m 次


假定要分析的字符串是:xfooxxxxxxfoo

  • .*foo (贪婪模式):
    模式分为子模式p1(.*)和子模式p2(foo)两个部分。其中p1中的量词匹配方式使用默认方式(贪婪型)。 匹配开始时,吃入所有字符xfooxxxxxxfoo去匹配子模式p1。匹配成功,但这样以来就没有了字符串去匹配子模式p2。本轮匹配失败;第二轮:减少p1部分的匹配量,吐出最后一个字符, 把字符串分割成xfooxxxxxxfoo两个子字符串s1和s2。 s1匹配p1, 但s2不匹配p2。本轮匹配失败;第三轮,再次减少p1部分匹配量,吐出两个字符,字符串被分割成xfooxxxxxxfooo两部分。结果同上。第四轮,再次减少p1匹配量, 字符串分割成xfooxxxxxxfoo两个部分,,这次s1/s2分别和p1/p2匹配。停止尝试,返回匹配成功。

  • .*?foo (勉强模式):

    最小匹配方式。第一次尝试匹配, p1由于是0或任意次,因此被忽略,用字符串去匹配p2,失败;第二次,读入第一个字符x,尝试和p1匹配,,匹配成功;字符串剩余部分fooxxxxxxfoo中前三个字符和p2也是匹配的。因此,停止尝试,返回匹配成功。在这种模式下,如果对剩余字符串继续去寻找和模式相匹配的子字符串,还会找到字符串末尾的另一个xfoo,而在贪婪模式下,由于第一次匹配成功的子串就已经是所有字符,因此不存在第二个匹配子串。

  • .*+foo (侵占模式):

    也叫占用模式。匹配开始时读入所有字符串,和p1匹配成功, 但没有剩余字符串去和p2匹配。因此,匹配失败。返回。

简单地说,贪婪模式和占有模式相比, 贪婪模式会在只有部分匹配成功的条件下,依次从多到少减少匹配成功部分模式的匹配数量,将字符留给模式其他部分去匹配;而占用模式则是占有所有能匹配成功部分,绝不留给其他部分使用。

占用模式目前只有java支持,通常比较少用。

2、Java实例

JDK中与正则表达式相关的类位于java.util.regex包下。有两个类PatternMatcher

        String regex = "((A+)(B(C)))";

        String input = "ABC-AABC-AAAAABCD";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);

        int groupCount = matcher.groupCount();
        // regex被圆括号分为四组,按照括号的出现位置,分别为:((A+)(B(C)))、(A+)、(B(C))、(C)
        // 匹配的时候,还是按照A+BC 去搜索,只不过,每次匹配成功后,会将搜到的字符串按照上面的四组规则分为四个子串,存储起来
        while (matcher.find()) {
            System.out.print("匹配结果:");
            for (int i = 0; i < groupCount; i++) {
                System.out.print("group" + i + ":" + matcher.group(i) + "\t");
            }
            System.out.println();
        }

输出:

匹配结果:group0:ABC group1:ABC group2:A group3:BC
匹配结果:group0:AABC group1:AABC group2:AA group3:BC
匹配结果:group0:AAAAABC group1:AAAAABC group2:AAAAA group3:BC

你可能感兴趣的:(JAVA基础之正则表达式)