1.单个字符的匹配规则如下:
正则表达式 | 规则 | 可以匹配 |
---|---|---|
A |
指定字符 | A |
\u548c |
指定Unicode字符 | 和 |
. |
任意字符 |
a ,b ,& ,0
|
\d |
数字0~9 |
0 ~9
|
\w |
大小写字母,数字和下划线 |
a `z`,`A`Z ,0 ~9 ,_
|
\s |
空格、Tab键 | 空格,Tab |
\D |
非数字 |
a ,A ,& ,_ ,…… |
\W |
非\w |
& ,@ ,中 ,…… |
\S |
非\s |
a ,A ,& ,_ ,…… |
2.多个字符的匹配规则如下
正则表达式 | 规则 | 可以匹配 |
---|---|---|
A* |
任意个数字符 | 空,A ,AA ,AAA ,…… |
A+ |
至少1个字符 |
A ,AA ,AAA ,…… |
A? |
0个或1个字符 | 空,A
|
A{3} |
指定个数字符 | AAA |
A{2,3} |
指定范围个数字符 |
AA ,AAA
|
A{2,} |
至少n个字符 |
AA ,AAA ,AAAA ,…… |
A{0,3} |
最多n个字符 | 空,A ,AA ,AAA
|
3.匹配指定范围
比如1A2b3c,我们可以这样写:[0-9a-fA-F],它表示一共可以匹配以下任意范围的字符:
0-9:字符0~9;
a-f:字符a~f;
A-F:字符A~F。
3.1排除法
^
不包含指定范围的字符
-
假设我们要匹配任意字符,但不包括数字,可以写
[^1-9]{3}
:可以匹配
"ABC"
,因为不包含字符1
~9
;可以匹配
"A00"
,因为不包含字符1
~9
;不能匹配
"A01"
,因为包含字符1
;不能匹配
"A05"
,因为包含字符5
。
4.或规则匹配
用|
连接的两个正则规则是或规则,例如,AB|CD
表示可以匹配AB
或CD
5.实用括号
现在我们想要匹配字符串learn java
、learn php
和learn go
怎么办?一个最简单的规则是learn\sjava|learn\sphp|learn\sgo
,但是这个规则太复杂了,可以把公共部分提出来,然后用(...)
把子规则括起来表示成learn\\s(java|php|go)
。
var 使用括号匹配大写 = "learn\\s([Jj]ava|[Gg]o|[Pp]hp)";
//匹配字符串learn Java、learn Php和learn Go的大小写
6.分组匹配
正则匹配区号-电话号
码这个规则
\d{3,4}\-\d{6,8}
如果需要提取区号和电话号码,需要将要提取的规则使用括号分组,把上述正则表达式变为(\d{3,4})\-(\d{6,8})
public class Main {
public static void main(String[] args) {
Pattern p = Pattern.compile("(\\d{3,4})\\-(\\d{7,8})");
Matcher m = p.matcher("010-12345678");
if (m.matches()) {
System.out.println(m.group(0));//010-12345678
String g1 = m.group(1);////010
String g2 = m.group(2);//12345678
System.out.println(g1);
System.out.println(g2);
} else {
System.out.println("匹配失败!");
}
}
}
使用Matcher
时,必须首先调用matches()
判断是否匹配成功,匹配成功后,才能调用group()
提取子串。
例子:
从字符串23:01:59
提取时、分、秒
var time = "([0-1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)";
Pattern p1 = Pattern.compile(time);
Matcher m1 = p1.matcher("23:01:59");
if (m1.matches()) {
System.out.println(m1.group(1));
System.out.println(m1.group(2));
System.out.println(m1.group(3));
} else {
System.out.println("匹配失败");
}
7.非贪婪匹配
给定一个字符串表示的数字,判断该数字末尾0
的个数。可以很容易地写出该正则表达式:(\d+)(0*)
"123000"
:3个0
期望结果:123
000
实际结果:"123000"
""
"10100"
:2个0
期望结果:101
00
实际结果:"10100"
""
"1001"
:0个0
期望结果:1001
""
实际结果:"1001"
""
这是因为正则表达式默认使用贪婪匹配:任何一个规则,它总是尽可能多地向后匹配,因此,\d+
总是会把后面的0
包含进来。
要让\d+
尽量少匹配,让0*
尽量多匹配,我们就必须让\d+
使用非贪婪匹配。在规则\d+
后面加个?
即可表示非贪婪匹配。
改写正则表达式如下(\d+?)(0*)"
因此,给定一个匹配规则,加上?
后就变成了非贪婪匹配。
我们再来看这个正则表达式(\d??)(9*)
,注意\d?
表示匹配0个或1个数字,后面第二个?
表示非贪婪匹配,因此,给定字符串"9999"
,匹配到的两个子串分别是""
和"9999"
,因为对于\d?
来说,可以匹配1个9
,也可以匹配0个9
,但是因为后面的?
表示非贪婪匹配,它就会尽可能少的匹配,结果是匹配了0个9
。
8.搜索和替换
8.1 分割字符串
System.out.println(Arrays.toString("a b c".split("\\s")));//[a, b, c]
System.out.println(Arrays.toString("a b c".split("\\s")));//[a, b, "", c]
System.out.println(Arrays.toString("a,b ;; c".split("[\\,\\s\\;]+")));//[a, b, c]
8.2 搜索字符串
String s = "the quick brown fox jumps over the lazy dog.";
Pattern p3 = Pattern.compile("\\wo\\w");// \w匹配[A-Za-z0-9_]
Matcher m3 = p3.matcher(s);
while (m3.find()) {
String sub = s.substring(m3.start(), m3.end());
System.out.println(sub);
//row fox dog
}
我们获取到Matcher
对象后,不需要调用matches()
方法(因为匹配整个串肯定返回false),而是反复调用find()
方法,在整个串中搜索能匹配上\\wo\\w
规则的子串,并打印出来。这种方式比String.indexOf()
要灵活得多,因为我们搜索的规则是3个字符:中间必须是o
,前后两个必须是字符[A-Za-z0-9_]
。
8.3 替换字符串
String s2 = "The quick\t\t brown fox jumps over the lazy dog.";
String r = s2.replaceAll("\\s+", " ");//将多个空格替换成一个
System.out.println(r); // "The quick brown fox jumps over the lazy dog."
8.4 反向引用
String s3 = "the quick brown fox jumps over the lazy dog.";
String r2 = s3.replaceAll("\\s([a-z]{4})\\s", "<b>$1</b>");
System.out.println(r2);//the quick brown fox jumps<b>over</b>the<b>lazy</b>dog.
它实际上把任何4字符单词的前后用<b>xxxx</b>
括起来。实现替换的关键就在于" <b>$1</b> "
,它用匹配的分组子串([a-z]{4})
替换了$1
。
8.5 使用Map替换模板中的值
HashMap<String, String> map = new HashMap<>();
map.put("name", "Mary");
map.put("lang", "Java");
String model = "Hello, ${name}! You are learning ${lang}!";
Pattern pt = Pattern.compile("\\$\\{([a-z]+)}");
Matcher mt = pt.matcher(model);
StringBuilder sb = new StringBuilder();
while (mt.find()) {
//0->${name} 1->name
//0->${lang} 1->lang
//0 代表整个匹配的字段 1代表匹配字段中的第一个分组
mt.appendReplacement(sb, map.get(mt.group(1)));
}
mt.appendTail(sb);
System.out.println(sb.toString());