一.正则表达式简介
正则表达式,本质上就是字符串,是一种规则,用来匹配,搜索,编辑,操作文本的,java中提供了java.util.regex包来与正则表达式进行模式匹配。
下面以一个简单的例子看看正则表达式,会有个更直观的印象:
public class RegexDemo1 {
public static void main(String[] args) {
Pattern p = Pattern.compile("[0-9]{2,3}");
Matcher m = p.matcher("123");
System.out.println(m.matches());// 输出: true
}
}
上述例子中[0-9]{2,3}就是所谓的正则表达式,{2,3}是匹配由至少2个数字,至多3个数字的字符串,前面[0-9]是指数字0-数字9,也就是十个数字。
java中提供了java.util.regex包,包里提供了两个类:Pattern和Matcher
Pattern就是一种模式,利用它的静态方法compile,编译正则表达式,得到一种匹配的模式,再利用matcher方法进行字符串的匹配,得到匹配结果Matcher,最后由Matcher处理结果。
二.Pattern类和Matcher类
Pattern类中提供了以下几个常用的方法:
static Pattern compile(String regex):将给定的正则表达式编译到模式中。
Matcher matcher(CharSequence input):创建匹配器,返回Matcher类实例。
String[] split(CharSequence input):将匹配到的数据拆分。
Matcher类中提供了以下几个常用的方法:
boolean matches():执行匹配验证,当整个匹配时才返回true。
String replaceAll(String replacement):替换字符串。
String group(int group):返回指定组的匹配数据。
三.字符串String类对正则的支持
字符串String类对正则表达式有四个内置的可以运行正则表达式的方法,分别是:matches(),split() ,replaceAll(),replaceFirst()
注意:replace()函数是不支持正则表达式的!
boolean matchers(String regex):当且仅当整个字符串匹配时返回true。
String[] split(String regex):根据给定的正则表达式的匹配来拆分字符串。
String replaceAll(String regex, String replacement):使用给定的replacement替换字符串所有匹配给定正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的replacement替换此字符串匹配给定的正则表达式的第一个子字符串。
public class RegexDemo2 {
public static void main(String[] args) {
String s = "hello java abc";
boolean s1 = s.matches("hello java");
System.out.println(s1);
System.out.println("------------------------");
String[] s2 = s.split("\\s");
for (String s0 : s2) {
System.out.println(s0);
}
System.out.println("-------------------------");
String s3 = s.replaceAll("\\s", "+");
System.out.println(s3);
System.out.println("-------------------------");
String s4 = s.replaceFirst("\\s", "-");
System.out.println(s4);
System.out.println("-------------------------");
}
}
输出结果:
false
------------------------
hello
java
abc
-------------------------
hello+java+abc
-------------------------
hello-java abc
-------------------------
四.正则表达式的规则
常见的匹配符号
.
匹配所有的单个字符,除了换行符。
^regex
匹配以字符串开头
regex$
匹配以字符串结尾
[abc]
复选集定义,匹配字符a,或b,或c
[^abc]
在中括号中插入^符号,表示否定模式
匹配所有的字符,除了a,b,c三个
[a-z1-9]
表示范围的匹配,匹配字母a到z和数字1到9,比如可以匹配到a4
a|b
或的选择,可以匹配a或者b,只会二选一
元字符
\d
匹配一个数字,相当于是[0-9]的简写,当要匹配一个数字时,可以直接写\d,简化了写[0-9]
\D
匹配一个非数字,相当于是[^0-9]的简写
\s
匹配一个空格,包括tab键(\t)
\S
匹配一个非空格
\w
匹配一个单词字符(包括大小写字母,数字,下划线),相当于是[a-zA-Z0-9_]的简写
\W
匹配一个非单词字符(就是除了大小写字母,数字和下划线之外的字符)
限定符
*:
匹配大于等于0个任意字符,可以是0个,也可以是无数个。
比如:X*表示匹配0个或者任意多个字符X,.*则表示匹配任何字符串
+:
匹配大于等于1个任意字符,至少是1个,可以是无数个。
?:
匹配0个或1个任意字符,至少是0个,至多是1个。
{n}:
表示只匹配n个任意字符。
{n,m}:
表示只匹配n-m个任意字符,最少n个,最多m个。
*?:
如果?是限定符*,+,?或者{}后面的第一个字符,那么表示这是一种非贪婪模式
非贪婪模式:就是指尽可能的少匹配,默认的是贪婪模式。
比如c(.*)b,默认是贪婪模式,是尽可能多匹配,而.*是匹配任意字个符,那么在匹配caabaabaab时,因为贪婪模式,中间的字符都属于匹配范围,只要最后一个b,最后匹配结果是:caabaabaab
而c(.*?)b,则是非贪婪匹配,是尽可能少的匹配,所以对于.*,应该是尽快能少的取,所以对于caabaabaab会被匹配,但是匹配得到是caab,中间尽量少匹配字符,所以在遇到第一个b时候结束匹配了。
public class RegexDemo3 {
public static void main(String[] args) {
Pattern p = Pattern.compile("c.*b");
Matcher m = p.matcher("caabaabaab");
if (m.find()) {
System.out.println("caabaabaab matches: " + m.group(0));
} // 输出:caabaabaab matches: caabaabaab
Pattern p1 = Pattern.compile("c.*?b");
Matcher m1= p1.matcher("caabaabaab");
if (m1.find()) {
System.out.println("caabaabaab matches: " + m1.group(0));
}// 输出:caabaabaab matches: caab
}
}
分组
使用小括号(),可以起到对正则表达式进行分组的作用,这样子我们可以使用Matcher类的group()函数来获取。
分组之后的引用也分为几种:
分组和反向引用
使用分组后会在正则表达式中创建反向引用,反向引用会保存匹配到的字符串片段,后续可以直接通过$来引用,$0表示匹配到的完整模式的字符串,$1表示第一个分组的反向引用,$2表示第二个分组的方向引用,依次类推.......
public class RegexDemo4 {
public static void main(String[] args) {
// 去除空格,得到Hello,java!
String str = "Hello , java !";
String p = "(\\w)(\\s+)([,!])";
System.out.println(str.replaceAll(p, "$1$3"));
// 输出:Hello, java!
}
}
解析:String p = "(\w)(\s+)([,!])";
这一个正则表达式分了三个组:
$0 会匹配(\w)(\s+)([,!])的结果,也就是o空格,和a空格!
$1 会匹配(\w)的结果,也就是o和a
$2 会匹配(\s+)的结果,也就是空格和空格
$3 会匹配[,!]的结果,也就是,和!
所以在调用replaceAll时,所有匹配到的结果,也就是o空格,会被替换为$1$3匹配到的结果o,,而a空格!也会被替换为$1$3匹配的结果a!
分组但不反向引用---(?😃
如果我们在小括号内开头加入?:,那么这个模式就只是分组,不会创建方向引用。
将上面代码做下小修改,如下:
public class RegexDemo4 {
public static void main(String[] args) {
// 去除空格,得到Hello,java!
String str = "Hello , java !";
String p = "(?:\\w)(\\s+)([,!])";
System.out.println(str.replaceAll(p, "$1$3"));
}
}
这样子将会报一个错误:
Exception in thread "main" java.lang.IndexOutOfBoundsException: No group 3
因为第一个分组(\w)被改为了(?:\w),只是分组,不会创建反向引用,所以这时候就没有3个反向引用了,只有两个,所以找不到group 3
分组的反向引用的副本
在使用反向引用时候,也可以不用$去引用,而是使用一个别名,也就是反向引用的副本,只要在小括号中加入?,就会把反向引用保存为一个名字为name的副本了,后续使用时候直接使用name就可以得到反向引用。
public class RegexDemo4 {
public static void main(String[] args) {
// 去除空格,得到Hello,java!
String str = "Hello,java";
Pattern p = Pattern.compile("(?\\w*),(?\\w*)");
Matcher m = p.matcher(str);
while (m.find()) {
System.out.println(m.group(1));// 输出:Hello
System.out.println(m.group("First"));// 输出:Hello
System.out.println(m.group(2));// 输出:java
System.out.println(m.group("Second"));// 输出:java
}
}
}
指定正则表达式的模式
可以在正则表达式语句的开头指定模式,有以下几种模式可以指定:
(?i):正则表达式忽略大小写。
(?s):表示单行模式(s表示single),那么正则表达式的.就会匹配所有字符,包括换行符了。
(?m):表示多行模式(m表示Multi-line),可以使用^和$匹配每一行的开始和结束。