正则表达式深入学习



以下是学习B站老韩的正则表达式专题视频总结,传送门。相关代码仓库地址:https://github.com/CoderBleu/learn_regular。

快速入门

初识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Demo {
public static void main(String[] args) {
String content = "1995年,互联网的蓬勃发展给了Oak机会。业界为了使死板、单调的静态网页能够“灵活”起来,急需一种软件技术来开发一种程序," +
"这种程序可以通过网络传播并且能够跨平台运行。于是,世界各大IT企业为此纷纷投入了大量的人力、物力和财力。这个时候," +
"Sun公司想起了那个被搁置起来很久的Oak,并且重新审视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编写的,所以非常小," +
"特别适用于网络上的传输系统,而Oak也是一种精简的语言,程序非常小,适合在网络上传输。Sun公司首先推出了可以嵌入网页并且可以随同网页在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术),并将Oak更名为Java(在申请注册商标时,发现Oak已经被人使用了,再想了一系列名字之后,最终,使用了提议者在喝一杯Java咖啡时无意提到的Java词语)。5月23日,Sun公司在Sun world会议上正式发布Java和HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Java平台。";

// 匹配文中所有单词,输出:找到:Oak 找到:IT 找到:Sun
// Pattern compile = Pattern.compile("[a-zA-Z]+");
// 匹配文中所有数字,输出:找到:1995
// Pattern compile = Pattern.compile("[0-9]+");
// 匹配数字或者单词
Pattern compile = Pattern.compile("([0-9]+)|([a-zA-Z]+)");
// 2.创建一个匹配器对象
Matcher matcher = compile.matcher(content);
// 3. 可以循环匹配
while (matcher.find()) {
// 匹配内容,文本,放到 m.group(0)
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
public class Demo {
public static void main(String[] args) {
String content = "2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发布。" +
"2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。在这些新特性当中,还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。" +
"2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0)," +
"代号为“Tiger”,Tiger包含了从1996年发布1.0版本以来的最重大的更新,其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、" +
"格式化I/O及可变参数。";

Pattern compile = Pattern.compile("\\d\\d\\d\\d");
Matcher matcher = compile.matcher(content);
// 查找过程:
// 1. 根据指定的规则,定位满足规则的字符串,比如2000
// 2. 找到后将 子字符串的开始索引 group[0]=0 记录到 matcher 对象的 int[] groups数组中;
// 3. 同时记录oldLast 的值为 子字符串的结束的 索引+1的位置值索引 group[1]=4,下次执行find是,就从记录的索引开始匹配
// 4. 如果matcher.group(i),i超过了范围,就会报索引越界异常,因为 getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
// 5. 继续向下找的时候同理, 找到后将 子字符串的开始索引记录 group[0]=65 到 matcher 对象的 int[] groups数组中;同时记录oldLast 的值为 子字符串的结束的 索引+1的位置值索引为group[1]=69,下次执行find是,就从记录的索引开始匹配
while (matcher.find()) {
// 开始匹配group - 源码:
// 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();
// }
System.out.println("找到:" + matcher.group(0));
}
}
}

查找过程:

  1. 根据指定的规则,定位满足规则的字符串,比如2000
  2. 找到后将 子字符串的开始索引 group[0]=0 记录到 matcher 对象的 int[] groups数组中;

Debug如下图所示,第一次匹配成功:

  1. 同时记录oldLast 的值为 子字符串的结束的 索引+1的位置值索引 group[1]=4,下次执行find是,就从记录的索引开始匹配
  2. 如果matcher.group(i),i超过了范围,就会报索引越界异常,因为 getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();

第二次匹配成功:

  1. 继续向下找的时候同理, 找到后将 子字符串的开始索引记录 group[0]=65 到 matcher 对象的 int[] groups数组中;同时记录oldLast 的值为 子字符串的结束的 索引+1的位置值索引为group[1]=69,下次执行find是,就从记录的索引开始匹配

原理剖析_带括号分组查询

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
34
35
36
37
38
public class Demo {
public static void main(String[] args) {
String content = "2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发布。" +
"2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。在这些新特性当中,还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。" +
"2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0)," +
"代号为“Tiger”,Tiger包含了从1996年发布1.0版本以来的最重大的更新,其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、" +
"格式化I/O及可变参数。";

Pattern compile = Pattern.compile("(\\d)(\\d\\d\\d)");
Matcher matcher = compile.matcher(content);
// 查找过程:
// 什么是分组,比如 (\d\d)(\d\d) ,则会功能则表达式中有 () 表示分组,第一个 ()表示第1组,第二个()表示第2组...
// 1. 根据指定的规则,定位欸满足规则的子字符串(比如(20)(00))
// 2. 找到后将 子字符串的开始索引 group[0]=0 记录到 matcher 对象的熟悉 int[] groups数组中;
// * 2.1 groups[0] = 0, 把该子字符串的结束的索引+1的值记录到 groups[1] = 4
// * 2.2 记录1组()匹配到的子字符串 groups[2] = 0 groups[3] = 2
// * 2.3 记录2组()匹配到的子字符串 groups[4] = 2 groups[5] = 4
// * 2.4 如果有更多的分组,同理
// 3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1的值即69,即下次执行find时,就从69开始匹配。
while (matcher.find()) {
// 开始匹配group - 源码:
// 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();
// }
System.out.println("找到:" + matcher.group(0)); // 2000
System.out.println("找到:" + matcher.group(1)); // 2
System.out.println("找到:" + matcher.group(2)); // 000
// System.out.println("找到:" + matcher.group(3)); 索引越界
}

}
}

首先先了解下什么是分组?比如 (\d\d)(\d\d) ,则会功能则表达式中有 () 表示分组,第一个 ()表示第1组,第二个()表示第2组…

查找过程:

  1. 根据指定的规则,定位欸满足规则的子字符串(比如(20)(00))
    1. 找到后将 子字符串的开始索引 group[0]=0 记录到 matcher 对象的熟悉 int[] groups数组中;
      • 2.1 groups[0] = 0, 把该子字符串的结束的索引+1的值记录到 groups[1] = 4
      • 2.2 记录1组()匹配到的子字符串 groups[2] = 0 groups[3] = 2
      • 2.3 记录2组()匹配到的子字符串 groups[4] = 2 groups[5] = 4

2.4 如果有更多的分组,同理

  1. 同时记录 oldLast 的值为 子字符串的结束的 索引+1的值即69,即下次执行find时,就从69开始匹配。

^ 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Demo {
public static void main(String[] args) {
String content = "abc$(a.bc(123(";

// ^ : 表示不在
//
// 匹配不在a到z的
Pattern compile = Pattern.compile("[^a-z]");
// 把连续符合匹配的一起输出,比如 $( ,(123(
// Pattern compile = Pattern.compile("[^a-z]+");
// 匹配不在 0-9 之间的一个字符
// Pattern compile = Pattern.compile("[^0-9]");
// 2.创建一个匹配器对象
Matcher matcher = compile.matcher(content);
// 3. 可以循环匹配
while (matcher.find()) {
// 匹配内容,文本,放到 m.group(0)
System.out.println("找到:" + matcher.group(0));
}
}
}

注意:在 ^ 为正则定位符时表示起始字符,比如^[0-9]+[a-z]*,表示以至少一个数字开头,然后任意小写字母字符串

转义字符的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo {
public static void main(String[] args) {
String content = "abc$(a.bc(123(";

// 俩个 \\ 字符表示 \
Pattern compile = Pattern.compile("\\(");
// 2.创建一个匹配器对象
Matcher matcher = compile.matcher(content);
// 3. 可以循环匹配
while (matcher.find()) {
// 匹配内容,文本,放到 m.group(0)
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
public class Demo {
public static void main(String[] args) {
String content = "a11c8abcAB\nCy_ABC abc@! .";

String reg = "[a-z]"; // 匹配 a-z之间任意一个字符
String reg1 = "[A-Z]"; // 匹配 A-Z之间任意一个字符
String reg2 = "abc"; // 匹配 abc字符串(默认区分大小写)
String reg3 = "(?!)abc"; // TODO 有问题 匹配 abc 字符串(不区分大小写),a(?!)bc表示bc不区分大小写
String reg4 = "[0-9]"; // 匹配 0-9之间任意一个字符
String reg5 = "[^a-z]"; // 匹配 不在 a-z之间任意一个字符
String reg6 = "[^0-9]"; // 匹配 不在 0-9之间任意一个字符
String reg7 = "[abcd]"; // 匹配 在 abcd中任意一个字符
String reg8 = "\\D"; // 匹配 不在 0-9的任意一个字符
String reg9 = "\\w"; // 匹配 大小写英文字母,数字,下划线
String reg10 = "\\W"; // 等价于 [^a-zA-Z0-9_],比如空格,感叹号!,艾特@
String reg11 = "\\s"; // 匹配任何空白字符(空格,制表符等)
String reg12 = "\\S"; // 表示,非空白就匹配,与\\s相反
String reg13 = "\\."; // "."匹配出 \n之外的所有字符,如果要匹配,本身则需要使用 \\.

Pattern compile = Pattern.compile(reg12);
// 启用不区分大小写的匹配。
// Pattern compile = Pattern.compile(reg2, Pattern.CASE_INSENSITIVE);
Matcher matcher = compile.matcher(content);
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
32
33
34
35
public class Demo {
public static void main(String[] args) {
String content = "<div class=\"avator selected\" data-uid=\"ea1c30cc\" data-uname=\"大良家的张先生\">\n" +
"<img data-name=\"大良家的张先生\" src=\"https://bkimg.cdn.bcebos.com/pic/e824b899a9014c086e0615d9f73715087bf40ad15e30?x-bce-process=image/resize,m_fill,w_110,h_110,align_50,limit_0/format,f_auto\" />\n" +
"<div class=\"avator_shd\"></div>\n" +
"</div>\n" +
"<div class=\"avator\" data-uid=\"2f941cd9\" data-uname=\"浅听寒雨\">\n" +
"<img data-name=\"浅听寒雨\" src=\"https://bkimg.cdn.bcebos.com/pic/3801213fb80e7bec54e7b7d7ca62ae389b504fc23dfa?x-bce-process=image/resize,m_fill,w_110,h_110,align_50,limit_0/format,f_auto\" />\n" +
"<div class=\"avator_shd\"></div>\n" +
"</div>\n" +
"<div class=\"avator\" data-uid=\"f2b1g7f2601000000\" data-uname=\"巧若南00u\">\n" +
"<img data-name=\"巧若南00u\" src=\"https://bkimg.cdn.bcebos.com/pic/38dbb6fd5266d0160924741e7267c30735fae6cd9ffa?x-bce-process=image/resize,m_fill,w_110,h_110,align_50,limit_0/format,f_auto\" />\n" +
"<div class=\"avator_shd\"></div>\n" +
"</div>\n" +
"<div class=\"avator\" data-uid=\"cc9495e7\" data-uname=\"三岁就很萌hhh\">\n" +
"<img data-name=\"三岁就很萌hhh\" src=\"https://bkimg.cdn.bcebos.com/pic/9345d688d43f8794a4c2af19375719f41bd5ad6e8cfd?x-bce-process=image/resize,m_fill,w_110,h_110,align_50,limit_0/format,f_auto\" />\n" +
"<div class=\"avator_shd\"></div>\n" +
"</div>\n" +
"<div class=\"avator\" data-uid=\"8c32b7e7\" data-uname=\"箬翊玖\">\n" +
"<img data-name=\"箬翊玖\" src=\"https://bkimg.cdn.bcebos.com/pic/a5c27d1ed21b0ef41bd57b66388846da81cb39db90fc?x-bce-process=image/resize,m_fill,w_110,h_110,align_50,limit_0/format,f_auto\" />\n" +
"<div class=\"avator_shd\"></div>\n" +
"</div>\n" ;

// 匹配 <img data-name="箬翊玖"中内容,比如:箬翊玖, \\S表示,非空白就匹配
Pattern compile = Pattern.compile("<img data-name=\"(\\S*)\"");
// 2.创建一个匹配器对象
Matcher matcher = compile.matcher(content);
// 3. 可以循环匹配
while (matcher.find()) {
// 匹配内容,文本,放到 m.group(0)
// System.out.println("找到:" + matcher.group(0)); 为:<img data-name="大良家的张先生"
System.out.println("找到:" + matcher.group(1));
}
}
}

基础语法图

进阶

捕获分组

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
public class Demo {
public static void main(String[] args) {
String content = "1324324332423";

/* 捕获分组*/
String reg1 = "(\\d\\d)(\\d\\d)"; // 匹配4个数字的字符串
// 命名分组:即可以给分组取名
String reg2 = "(?<g1>\\d\\d)(?<g2>\\d\\d)";

Pattern pattern = Pattern.compile(reg2);
Matcher matcher = pattern.matcher(content);

while (matcher.find()) {
/*找到:1324
找到第一个分组的第一个内容:13
找到第一个分组的第一个内容:24*/
System.out.println("找到捕获分组:" + matcher.group(0));
System.out.println("找到捕获分组的第一个内容:" + matcher.group(1));
System.out.println("找到捕获分组的第二个内容:" + matcher.group(2));

/*命名分组方式*/
// System.out.println("找到捕获分组:" + matcher.group(0));
// System.out.println("找到第捕获分组的第一个内容:" + matcher.group("g1"));
// System.out.println("找到第捕获分组的第二个内容:" + matcher.group("g2"));

}

}
}

非捕获分组

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
public class Demo {
public static void main(String[] args) {

/* 非捕获分组,注意不能 matcher.group(1) */
String content1 = "梦想天空分外蓝啊、分外蓝呀、分外蓝";

// 等同于 "分外蓝|分外蓝啊|分外蓝呀"
// 输出:找到非捕获分组:分外蓝啊 找到非捕获分组:分外蓝呀
String reg3 = "分外蓝(?:啊|呀)";

// 找到分外蓝关键字,但是要求只是查询 分外蓝啊 和 分外蓝呀 中的分外蓝
// 输出:找到非捕获分组:分外蓝
String reg4= "分外蓝(?=啊|呀)";

// 找到分外蓝关键字,但是要求只是查询匹配 不是 分外蓝啊 和 分外蓝呀 中包含有的分外蓝
// 输出:找到非捕获分组:分外蓝啊 找到非捕获分组:分外蓝呀
String reg5= "分外蓝(?:啊|呀)";

Pattern pattern = Pattern.compile(reg5);
Matcher matcher = pattern.matcher(content1);

while (matcher.find()) {
System.out.println("找到非捕获分组:" + matcher.group(0));
}

}
}

非贪婪匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Demo {
public static void main(String[] args) {
String content = "hello 1oo123111";

// String reg = "\\d+"; //默认是贪婪匹配,找到:1,找到:123111
String reg = "\\d+?"; //非贪婪匹配,找到:1,找到:1,找到:2...

Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(content);

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
public class Demo {
public static void main(String[] args) {
// String content = "h1234e155111111abad12321-33344455512211";
String content = "1221 1111 1111 123213";

// 匹配两个连续的相同数字:(\\d)\\1
String reg1 = "(\\d)\\1";
// 匹配五个连续的相同的数字 11111
String reg2 = "(\\d)\\1{4}";
// 匹配个位与千位相同,十位与百位相同的数,匹配1111
String reg3 = "(\\d)(\\d)\\2\\1";
// 匹配前面是一个五位数,然后一个 -号,然后是一个九位数,连续的每三位要相同,匹配12321-333444555
String reg4 = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
// 匹配123213,(\\d)(\\d)(\\d)\\2\\1中 \\2表示反向引用123三个数字中的第二位2,同理\\1,\\3也是如此
String reg5 = "(\\d)(\\d)(\\d)\\2\\1\\3";

Pattern compile = Pattern.compile(reg5);
Matcher matcher = compile.matcher(content);
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
public class Demo {
public static void main(String[] args) {
String content = "21lauyhelloworld hilauy lauyu Hilauy123";

// 以至少1个数字开头,后接0或者任意个小写字符串
String reg1 = "^[0-9]+[a-z]*";
// 以至少1个数字开头,必须以至少一个小写字母结束,比如仅仅跟 2-lauyhelloworld 匹配,就符合要求
String reg2 = "^[0-9]+\\-[a-z]+$";

// 匹配一个单词边界,即字与空格间的位置。如果它位于要匹配的字符串的开始,它在单词的开始处查找匹配项。如果它位于字符串的结尾,它在单词的结尾处查找匹配项。
// 上述例子,我们可以找字符串结尾处的,比如 21lauyh...,lauyu,lauy123
String reg3 = "lauy\\b";

// 和\\b含义相反,详细:面表达式匹配 minelauyo 中的字符串 apt,但不匹配 lauyo 中的字符串 apt:
String reg4 = "lauy\\B";

Pattern pattern = Pattern.compile(reg4);
Matcher matcher = pattern.matcher(content);

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
32
33
34
public class Demo {
public static void main(String[] args) {
String content = "111112141aaaaa1ahello";

String reg1 = "a{3}"; // 表示匹配 aaa
String reg2 = "1{4}"; // 表示匹配 1111
String reg3 = "\\d{2}"; // 表示匹配 两位的任意数字字符

// 细节:java匹配默认贪婪匹配,尽可能的匹配多的
String reg4 = "a{3,4}"; //表示匹配aaa 或者 aaaa,优先aaaa多的
String reg5 = "1{4,5}"; // 表示匹配 1111 或者 11111
String reg6 = "\\d{2,5}"; // 匹配2位数或者3,4,5,如下会找到找到:11111 和 11

// + 号使用
String reg7 = "1+"; // 匹配一个1或者多个1
String reg8 = "\\d+"; // 匹配一个或多个数字
String reg11 = "\\d*"; // 匹配0个或多个数字

// * 号使用
String reg9 = "1*"; // 匹配0个1或者多个1

// ? 号使用,遵守贪婪匹配
String reg10 = "a1?"; // 匹配 a 或者 a1


Pattern pattern = Pattern.compile(reg11, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(content);

while (matcher.find()) {
System.out.println("找到:" + matcher.group(0));
}

}
}

选择匹配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo {
public static void main(String[] args) {
String content = "lauy 木艺术";
String regStr = "lauy|木|艺";

Pattern pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(content);

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
public class Demo {
public static void main(String[] args) {
String content = "我我我要学学学编程java!!!";

String reg = "(.)\\1+";
Pattern compile = Pattern.compile(reg);
Matcher matcher = compile.matcher(content);
while (matcher.find()) {
System.out.println("匹配格式:" + matcher.group(0));
}

// 使用 反向引用$1 来替换匹配的内容
String result = Pattern.compile(reg).matcher(content).replaceAll("$1");
System.out.println("去重后:" + result);
}
}

验证是否为整数或小数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo {
public static void main(String[] args) {
// 比如:123 -1212 34.35 -34.52 -0.01 0.03等
String content = "-0.89";
/**
* [+-]? 表示可以是 +正数 或者 -负数,甚至没有符号的正数
* [0|[1-9]\\d*] 表示 0 或者 以1到9开头后面可以是 0个或者多个数字
* (\\.\\d+) 表示 开头接 .符号,再可以一个或者多个数字
* ? 表示可以0个或者1个
* $ 以什么结尾,此处是以数字结尾
*/
String reg = "[+-]?[0|[1-9]\\d*](\\.\\d+)?$";

if (content.matches(reg)) {
System.out.println("匹配成功");
} else {
System.out.println("匹配失败");
}
}
}

正则匹配URL

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
public class Demo {
public static void main(String[] args) {
String content = "https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9599029349875218161%22%7D&n_type=0&p_from=1";

/**
* 注: \\w:匹配 大小写英文字母,数字,下划线_
* 1. 匹配 https:// 或者 http:// :^((http|https)://) 其中^ 表示至少有一个 http 或者 https开头,
* (http|https)分组表示http 或者 https
* 2. 可以匹配不带协议的 url,比如:mbd.baidu.com/newspage/data/landingsuper,
* 用 正则限定符 ?,指定匹配出现0次或者1次,^((http|https)://)?
* 3. 匹配 mbd.baidu.com,使用:([\\w-]+\\.)+[\\w]+ ,其中 [\\w-]+ 表示匹配 www-top.com 这种,
* 中间允许 \\w格式 或者 -,+表示匹配指定字符出现1次多种多次
* 4. 匹配后面所有 /newspage , [*]在中括号里表示匹配 * 符号
* 表示 (\\/[\\w-=%.&?]*) 匹配 / 开头,后面接 \\w 和 - 或者 = 或者 % 或者 . 或者 & 或者 ? 格式
* 5. 第四步(\\/[\\w-=%.&?]*)后面接*表示匹配重复出现0次或者多次,
* 6. 然后$ ,表示以 /asfssba 这种格式结尾
*/
// String reg = "^((http|https)://)?([\\w-]+\\.)+[\\w]+(\\/[\\w-=%.&?]*)?$";
String reg = "^((http|https)://)?([\\w-]+\\.)+[\\w]+(\\/[\\w-=%.&?]*)*$";

Pattern compile = Pattern.compile(reg);
Matcher matcher = compile.matcher(content);
if (matcher.find()) {
System.out.println("匹配格式" + matcher.group(0));
} else {
System.out.println("不匹配格式");
}
}
}

常用

校验数字的表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
数字:^[0-9]*$
n位的数字:^\d{n}$
至少n位的数字:^\d{n,}$
m-n位的数字:^\d{m,n}$
零和非零开头的数字:^(0|[1-9][0-9]*)$
非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
非负整数:^\d+$ 或 ^[1-9]\d*|0$
非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$

校验字符的表达式

1
2
3
4
5
6
7
8
9
10
11
12
汉字:^[\u4e00-\u9fa5]{0,}$
英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
长度为3-20的所有字符:^.{3,20}$
26个英文字母组成的字符串:^[A-Za-z]+$
26个大写英文字母组成的字符串:^[A-Z]+$
26个小写英文字母组成的字符串:^[a-z]+$
由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
禁止输入含有~的字符:[^~\x22]+

特殊需求表达式

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
34
Email地址:^\w+@[a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码1:^(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])\d{8}$ (由于工信部放号段不定时,所以建议使用泛解析 ^([1][3,4,5,6,7,8,9])\d{9}$)
手机号码2:^[1][3,4,5,6,7,8,9][0-9]{9}$
电话号码("XXX-XXXXXXX""XXXX-XXXXXXXX""XXX-XXXXXXX""XXX-XXXXXXXX""XXXXXXX""XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
18或15位身份证号码(数字、字母x结尾):^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[\da-zA-Z]{8,10}$
日期格式:^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
钱的输入格式:
1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "" 的 "10000" 和 "10,000":^[1-9][0-9]*$
2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式:[\u4e00-\u9fa5]
双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:\n\s*\r (可以用来删除空白行)
HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)) (由@飞龙三少 提供,感谢共享)

常用为引用 - 原文出处:作者:zxin

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  1. © 2020-2021 Lauy    湘ICP备20003709号

请我喝杯咖啡吧~

支付宝
微信