Java中使用正则表达式

前言

正则在线测试工具

https://c.runoob.com/front-end/854/

Java语言中的正则表达式匹配功能主要是通过java.util.regex.Matcher类和以下这些方法实现的。

  • find():在一个字符串里寻找一个给定模式的匹配。
  • lookingAt(): 用一个给定的模式去尝试匹配一个字符串的开头。
  • matches():用一个给定的模式去尝试匹配一个完整的字符串。
  • replaceAll():进行替换操作,对所有的匹配都进行替换。
  • replaceFirst():进行替换操作,只对第一个匹配进行替换。
    matcher类还提供了几个能够让程序员对特定操作做出更细致调控的方法。此外,java.util.regex.pattern类也提供了几个简单易用的包装器方法。
  • compile():把一个正则表达式编译成一个模式。
  • flags():返回某给定模式的匹配标志。
  • matches():在功能上等价于刚才介绍的matches()方法。Pattern类的构造方法是私有的,所以我们使用Pattern p = Pattern.compile("a*b")进行实例化;Matcher类的实例化依赖Pattern类的对象Matcher m = p.matcher("aaaaab");
  • pattern():把一个模式还原为一个正则表达式。
  • split():把一个字符串分为子字符串。

String本身的方法

在实际的开发中,为了方便我们很少直接使用Pattern类或Matcher类,而是使用String类下的方法

  • 验证:boolean matches(String regex)
  • 拆分: String[] split(String regex)
  • 替换: String replaceAll(String regex, String replacement)

注意事项

Sun公司发布的Java正则表达式支持与Perl语言基本兼容,但要注意以下几点。

  • 要想使用正则表达式,必须先用import java.util.regex.* 语句导入正则表达式组件(这条语句将导入一个完整的软件包。如果你只需要用到其中的一部分功能,请用相应的软件包名字替换掉这条语句里的*)。
  • 不支持嵌入条件。
  • 不支持使用\E\l\L\u\U进行字母大小写转换。
  • 不支持使用\b匹配退格符。
  • 不支持\z

常用的语法

字符的取值范围

  • [abc] : 表示可能是a,可能是b,也可能是c。

  • [^abc]: 表示不是a,b,c中的任意一个

  • [a-zA-Z]: 表示是英文字母

  • [0-9]: 表示是数字

简洁的字符表示

  • .:匹配任意的字符
  • \d:表示数字
  • \D:表示非数字
  • \s:表示由空格组成,[ \t\n\r\x\f]
  • \S:表示由非空字符组成,[^\s]
  • \w:表示字母、数字、下划线,[a-zA-Z0-9_]
  • \W:表示不是由字母、数字、下划线组成
  • \b:匹配一个字边界,即字与空格间的位置。例如,”er\b”匹配”never”中的”er”,但不匹配”verb”中的”er”。
  • \B:非字边界匹配。”er\B”匹配”verb”中的”er”,但不匹配”never”中的”er”。

数量表达式

  • ?: 表示出现0次或1次
  • +: 表示出现1次或多次
  • *: 表示出现0次、1次或多次
  • {n}:表示出现n次
  • {n,m}:表示出现n~m次
  • {n,}:表示出现n次或n次以上
  • ?:当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是”非贪心的”。”非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的”贪心的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串”oooo”中,”o+?”只匹配单个”o”,而”o+”匹配所有”o”。

注意:

*, +, {n,}都是常用的贪婪型元字符,在匹配时它们会尽可能地从一段文本的开头一直匹配到这段文本的末尾,而不是从这段文本的开头匹配到碰到第一个匹配时为止。

逻辑表达式

  • XY: 表示X后面跟着Y,这里X和Y分别是正则表达式的一部分
  • X|Y:表示X或Y,比如”food|f”匹配的是foo(d或f),而”(food)|f”匹配的是food或f
  • (X):子表达式,将X看做是一个整体

多用途元字符

*:只有当它出现在一个字符集合里(被放在[和]之间)并紧跟在左方括号[的后面时,它才能发挥“求非”作用。如果是在一个字符集合的外面并位于一个模式的开头,^将匹配字符串的开头。
例如:

[^abc]: 表示不是a,b,c中的任意一个;
^\s*<\?xml.*\?>:匹配一个<?xml>标签内容,并且该内容出现在字符串的开头
相应的,$匹配字符串的结尾,

\s*$匹配一个字符串结尾处的零个或多个空白字符

回溯引用匹配:前后一致匹配

例如:
文本:

1
2
3
<H1>ColdFusion</H1>
<H2>ColdFusion</H2>
<H2>This is not valid HTML</H3>

正则表达式:

1
<[hH][1-6]>.*?</[hH][1-6]>

结果:

1
2
3
<H1>ColdFusion</H1>
<H2>ColdFusion</H2>
<H2>This is not valid HTML</H3>

回溯引用在替换操作中的应用

如下例:
文本:

1
Hello, ben@forta.com is my email address!

正则表达式:

1
\w+[\w.]*@[\w.]+\.\w+

结果:

image-20230112092916984

文本:

1
Hello, ben@forta.com is my email address!

正则表达式:

1
(\w+[\w.]*@[\w.]+\.\w+)

替换:

1
<A HREF="mailto:$1">$1</A>

Java代码:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
// 1. 回溯引用在替换操作中的应用
String str = "Hello, ben@forta.com is my email address!";
String regex = "(\w+[\w.]*@[\w.]+\.\w+)";
Pattern pat = Pattern.compile(regex); //实例化Pattern对象
Matcher mat = pat.matcher(str); //实例化Matcher对象
while(mat.find()) {
System.out.println(mat.replaceAll("<A HREF=\"mailto:$1\">$1</A>"));
}
}

结果:
img

转义字符

在其他语言中,\\ 表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。

在 Java 中,\\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。

所以,在其他的语言中(如 Perl),一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\

1
2
System.out.print("\\");    // 输出为 \
System.out.print("\\\\"); // 输出为 \\

代码示例

匹配

假如文本时

1
<<:name:>>+<<:age:>>

正则

1
<<:(\w+?):>>

结果

image-20230112103916886

匹配

1
2
3
4
5
6
7
Pattern pattern = Pattern.compile("<<:(\\w+?):>>");
Matcher matcher = pattern.matcher("<<:name:>>+<<:age:>>");
while (matcher.find()) {
for (int i = 0; i <= matcher.groupCount(); i++) {
System.out.println(matcher.group(i));
}
}

结果

1
2
3
4
<<:name:>>
name
<<:age:>>
age

替换

1
2
String str = "<<:name:>>+<<:age:>>".replaceAll("<<:(\\w+?):>>", "$1");
System.out.println(str);

结果

1
name+age

注意

$0是整体匹配

$1为第一个括号匹配的内容

是否匹配

1
2
3
4
5
public static void main(String[] args) {
String str = "<<:id:>>*2+1";
boolean isMatch = str.matches("<<:(\\w+?):>>");
System.out.println(isMatch);
}

结果是false

如果是下面的

1
2
3
4
5
public static void main(String[] args) {
String str = "<<:id:>>";
boolean isMatch = str.matches("<<:(\\w+?):>>");
System.out.println(isMatch);
}

结果是true

也就是说

matches是完全匹配。