java + 正则表达式(实现正则匹配)

在这里首先说明一点,正则表达式和编程语言(java)是没有什么关系的,它是一种字符串匹配的模式,或者说是一种被广泛认可的规范。不同的编程语言一般都有正则匹配的函数或者对象,例如在java中,使用正则的两个主要的类就是Pattern和Matcher。

1
Matcher m = Pattern.compile(">(\\w+)<").matcher(a);

使用过正则表达式的朋友一般都是在网上摘抄正则表达式,然而对一眼看上去乱七八糟的表达式也看不懂,其实正则表达式的难点就两个:捕获型匹配和非捕获型匹配

一般的正则表达式元字符查询网上的教程就可以。

https://www.runoob.com/regexp/regexp-metachar.html

比较常用的和容易忘记的我在这儿记录如下:

  • 贪婪匹配 相当于{0,} 0个到多个

    ? 非贪婪匹配 相当于{0,1}

    \b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配”never” 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。

    \B 匹配分单词边界

    [xyz] 匹配任意一个字符,但一定是一个字符

    [^xyz] 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’
    可以匹配 “plain” 中的’p’、’l’、’i’、’n’。这个一定要区别于字符开头 ^

    \D 匹配一个非数字字符

    \s 匹配任何非空白字符

    \w 字符数组下划线

然后重点说一下捕获型匹配非捕获型匹配

捕获型匹配

(pattern) 匹配 pattern 并获取这一匹配。
既然还获取了匹配到的字符串,那我们获取到的字符串肯定是有用的。有什么用呢?我们可以通过分组打印出来,也可以在同一个表达式中,引用这个分组的内容。

补充说一下:

分组:

从正则表达式左侧开始,每出现一个左括号”(“记做一个分组,分组编号从 1 开始。0 代表整个表达式。

如果正则表达式中没有捕获型匹配(),则只有分组0,分组0代表匹配到的整个字符串。

例如:日期的正则(\d{4})-(\d{2})-(\d{2}) 能匹配2020-01-01

1
2
3
4
5
6
7
8
9
10
String a = "2020-01-01";
Matcher m = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})").matcher(a);
if(m.find()){
System.out.println(m.group(0));
System.out.println(m.group(1));
System.out.println(m.group(2));
System.out.println(m.group(3));
}else{
System.out.println(false);
}

以上程序将打印

1
2
3
4
2020-01-01
2020
01
01

那如何引用分组呢?
比如说我们想要拿到2月2号 3月3号 4月4号这样的数据

1
2
3
4
5
6
7
String a = "2020-05-05";
Matcher m = Pattern.compile("(\\d{4})-(\\d{2})-\\2").matcher(a);
if(m.find()){
System.out.println(m.group(0));
}else{
System.out.println(false);
}

这样也能匹配到a字符串

非捕获型匹配

如果搞清楚了捕获型数组那么非捕获型匹配就很容易理解了,他们的区别从字面意思上也可以看出来,非捕获型数组会参与匹配,但是不会被捕获。

元字符表达如下:(?=匹配内容)

1
2
3
4
5
6
7
String a = "2020-05-05";
Matcher m = Pattern.compile("(\\d{4})-(\\d{2})-(?=\\2)").matcher(a);
if(m.find()){
System.out.println(m.group(0));
}else{
System.out.println(false);
}

输出结果为

1
2020-05-

以上代码我们用到了非捕获型数组“(?=\2)” 表达的意思是取到第二个分组的内容结合前面的表达式一起进行匹配,但是匹配的结果字符串,不包含非捕获型数组匹配的字符子串。

例子2

1
Pattern.compile("windows(?=2003)").matcher(a);

能匹配”windows2003”中的”windows” 但是不能匹配 “windows2010”中的windows




理解了以上,我们用java结合正则实现一个算法:

题目如下:

删除整数中重复出现的数字。

答案如下:

1
2
3
4
5
6
String str = "156156456";
Matcher m = Pattern.compile("(\\d)(?=.*\\1)").matcher(str);
//()捕获型数组,(?=)非捕获性数组, *匹配前面的0次或者多次,\1捕获型数组拿到的值
while (m.find()){
str = str.replace(m.group(), "");
}

以后遇到正则表达式不用一脸懵逼了,还可以自己使用正则表达式完成一些特殊的匹配。