一、代码逻辑
public class Test2 {
public static void main(String[] args) {
String s = "一百多年后,四色问题仍未解决。1969年,Heinrich Heesch发表了一个用计算机" +
"解决此问题的方法。1976年,阿佩尔(Appel)和哈肯(Haken)借助计算机给出了一个证明," +
"此方法按某些性质将所有地图分为1936类并利用计算机,运行了1200个小时,验正了它们可" +
"以用四种颜色染色。四色定理是第一个主要由电脑证明的理论,这一证明并不被所有的数学家接受" +
",因为采用的方法不能由人工直接验1111证。最终,人们必须对电脑编译的正确性以及运行这一程序" +
"的硬件设备充分信任。主要是因为此证明缺乏数学应1112有的规范,以至于有人这样评论“一个好的数" +
"学证明应6666当像一首诗——而3333这纯粹是一本电话簿!";
String regStr = "\\d\\d\\d\\d";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println("找到: " + matcher.group(0));
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
二、底层逻辑分析
1.matcher.find()
matcher.find()完成的任务
1.根据匹配规则,定位满足规则的子字符串(比如1969)
2.找到后,将子字符串的开始索引记录到 matcher 对象的属性 int[] groups,groups[0]=0,
把该子字符串的结束的索引+1的值记录到groups[1]=4
如果正则表达式有分组,则groups[2] groups[3]记录第一组字符串的开始和结束位置
其余组以此类推
3.同时记录 oldLast 的值,即该子字符串的结束的索引+1的值,即下次执行find时,就从4开始匹配
4.当一次匹配成功后,下一次groups[0]存储下一个符合条件的字符串的开始位置,groups[1]存储的
是其结束位置 周而复始直到文本遍历结束
2.matcher.group(0)
public String group(int group) {
if (first < 0)
throw new IllegalStateException("No match found");
if (group < 0 || group > groupCount())
throw new IndexOutOfBoundsException("No group " + group);
if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
return null;
return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
三、正则表达式语法
1.转义符
\\
在我们使用正则表达式去检索某些特殊字符时,需要用到转移符号,负责检索不到
在java的正则表达式中,两个 \\ 代表其他语言中的一个 \
需要用到转移符号的字符有如下字符:
. * + ( ) $ / \ ? [ ] ^ { }
private static void test(){
String s = "abc.$(abc.(123(";
String regStr = "\\(";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
2.字符匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|
[ ] | 可接收的字符列表 | [abcew] | 匹配a b c e w中的任意一个字符 | |
[^] | 不接收的字符列表 | [^abc] | 匹配除a b c之外的任意一个字符,包括数字和特殊字符 | |
- | 连接符 | a-z | 匹配任意单个小写字母 | |
. | 匹配除 \n 以外的任何字符 | a…b | 匹配以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 | aaab #a1o |
\\d | 匹配单个数字字符,相当于[0-9] | \\d{3}(\\d)? | 包含3个或4个数字的字符串 | 123 4351 |
\\D | 匹配单个非数字字符,相当于[^0-9] | \\D(\\d)* | 以单个非数字字符开头,后接任意个数字字符串 | a #124 |
\\w | 匹配单个数字、大小写字母、下划线字符,相当于[0-9a-zA-Z] | \\d{3}\\w{4} | 以3个数字字符开头的长度为7的字符串 | 123nmed 4322swe |
\\W | 匹配单个非 数字、大小写字母下划线字符,相当于[^0-9a-zA-Z] | \\W+\\d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串 | #23 %()10 |
\\s | 匹配任何空白字符(空格 制表符等) | \\s | | |
\\S | 匹配任何非空白字符,与\\s相反 | \\S | | |
java匹配字母字符时默认区分大小写,可以使用以下方法设置不区分大小写
(1)(?i)
(?i)abc 表示abc都不区分大小写
a(?i)bc 表示bc不区分大小写
a((?i)b)c 表示只有b不区分大小写
(2)Pattern.CASE_INSENSITIVE 在创建模式对象时加这个
Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
private static void test1(){
String s = "a11c8AQ4Wz";
String regStr = "[a-z]";
Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
3.选择匹配符
符号 | 含义 | 示例 | 解释 |
---|
| | 匹配 | 之前或之后的表达式 | ab|cd | ab或者cd |
private static void test2(){
String s = "1231one一";
String regStr = "1|one|一";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
4.限定符
用于指定其前面的字符和组合项连续出现多少次
符号 | 含义 | 示例 | 说明 | 匹配输入 |
---|
* | 指定字符重复0次或者n次(无要求) | (abc)* | 仅包含任意个abc的字符串 | abc abcabc |
+ | 指定字符重复1次或者n次 | m+(abc)* | 以至少1个m开头,后接任意个abc的字符串 | m mabc mabcabc |
? | 指定字符重复0次或者1次 | m+abc? | 以至少1个m开头,后接ab或abc的字符串 | mab mabc mmab |
{n} | 只能输入n个字符 | [abcd]{3} | 由abcd中字母组成的任意长度为3的字符串 | abc dbc adc |
{n,} | 指定至少n个匹配 | [abcd]{3,} | 由abcd中字母组成的任意长度不小于3的字符串 | aab dbc aaadca |
{n,m} | 指定至少n个但不多于m个匹配 | [abcd]{3,5} | 由abcd中字母组成的任意长度不小于3 不大于5的字符串 | abc abcd aaaaa cdba |
java中默认是贪婪匹配:a{3,5} 在匹配过程中尽量多的匹配a,匹配优先级:aaaaa > aaaa > aaa
private static void test3(){
String s = "12adee3421dsacc543";
String regStr = "\\d+";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
5.定位符
规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置
符号 | 含义 | 示例 | 说明 | 匹配输入 |
---|
^ | 指定起始字符 | +[a-z]* | 以至少1个数字开头,后接任意个小写字母的字符串 | 123 3ac |
$ | 指定结束字符 | +[a-z]+$ | 以至少1个数字开头,并以至少1个小写字母结尾的字符串 | 11a |
\\b | 匹配目标字符串的边界 | hello\\b | 边界:子串间有空格,或者是目标字符串的结束位置 | |
\\B | 匹配目标字符串的非边界 | hello\\B | 和\\b的含义相反 | |
6.分组
常用分组构造形式 | 说明 |
---|
(pattern) | 非命名捕获 捕获匹配的子字符串 编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号 |
(?pattern) | 命名捕获 将匹配的子字符串捕获到一个组名或者编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替<> |
| |
private static void test4(){
String s = "1234adace2314de9999";
String regStr = "(\\d\\d)(\\d\\d)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.print("整个内容: " + matcher.group(0) + " ");
System.out.print("第1个分组内容: " + matcher.group(1) + " ");
System.out.println("第2个分组内容: " + matcher.group(2));
}
}
7.反向引用
反向引用:圆括号的内容被捕获后,可以在这个括号后被使用
这种引用既可以是在正则表达式的内部,也可以是在正则表达式的外部
内部反向引用使用 \\分组号 ,外部反向引用使用 $分组号
举例:
匹配两个连续的相同数字:(\\d)\\1
匹配五个连续的相同数字:(\\d)\\1{4}
匹配个位与千位相同,十位与百位相同的数:(\\d)(\\d)\\2\\1
private static void test11(){
String s = "1111and2332sdniew1234kd13";
String regStr = "(\\d)(\\d)\\2\\1";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println("找到: " + matcher.group(0));
}
}
private static void test12(){
String s = "12321-333999111";
String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println("找到: " + matcher.group(0));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
四、常用的类
1.Pattern类
private static void test10(){
String s = "hello welcome to china";
String regStr = ".*come.*";
boolean matches = Pattern.matches(regStr, s);
System.out.println("整体匹配: " + matches);
}
2.Matcher类
五、应用实例
1.验证输入的字符串是不是汉字
private static void test5(){
String s = "为中华之崛起而读书";
String regStr = "^[\u0391-\uffe5]+$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
2.验证邮政编码
private static void test6(){
String s = "123540";
String regStr = "^[1-9]\\d{5}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
3.验证QQ号
private static void test7(){
String s = "1904459082";
String regStr = "^[1-9]\\d{4,9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
4.验证手机号码
private static void test8(){
String s = "15109546897";
String regStr = "^1[3|4|5|8]\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
5.验证URL地址
private static void test9(){
String s = "https://leetcode.cn/problemset/algorithms/?status=NOT_STARTED&page=1";
String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/.]*)?$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
6.结巴程序
例如 我...是...你你..爸爸爸爸...
输出 我是你爸爸
private static void test13(){
String s = "我我我...是是是是是...你你..爸爸爸爸...";
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = pattern.matcher(s);
s = matcher.replaceAll("");
pattern = Pattern.compile("(.)\\1+");
matcher = pattern.matcher(s);
while (matcher.find()){
}
s = matcher.replaceAll("$1");
System.out.println(s);
s = Pattern.compile("(.)\\1+").matcher(s).replaceAll("$1");
System.out.println(s);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
6.String类的替换分割匹配
private static void test14(){
String s = "JDK1.3是JDK1.4之前的一个版本,JDK1.3与JDK1.4之间有一些区别";
s = s.replaceAll("JDK1\\.3|JDK1\\.4","JDK");
System.out.println(s);
}
private static void test15(){
String s = "13811112222";
if (s.matches("1(38|39)\\d{8}")){
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
}
private static void test16(){
String s = "hello-boy~welcome#1to3china";
String[] splits = s.split("#|-|~|\\d+");
for (String cur : splits){
System.out.print(cur + " ");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33