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期望结果:123000实际结果:"123000""""10100":2个0期望结果:10100实际结果:"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());