正则表达式
目录
本文案例均来自于B站的《【韩顺平讲Java】Java 正则表达式专题 -正则 正则表达式 元字符 限定符 Pattern Matcher 分组 捕获 反向引用等》
传送门
入门案例
import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.*;
/**
* @author computer
*/
public class RegExpressionTest {
/**
* 验证:正则表达式体验 - 找到文本中所有的英文字母和数字
*/
@Test
public void test1() {
String str = "1994年6月,在同约翰·盖吉、詹姆斯·高斯林、比尔·乔伊、帕特里克·诺顿、韦恩·罗斯因和埃里克·施密特经历了一场历时三天的头脑风暴后,团队决定再一次改变努力的目标,这次他们决定将该技术应用于万维网。他们认为随着Mosaic浏览器的到来,因特网正在向同样的高度互动的远景演变,而这一远景正是他们在有线电视网中看到的。作为原型,帕特里克·诺顿写了一个小型万维网浏览器,WebRunner,后来改名为HotJava[13]。\n" +
"\n" +
"1994年10月,HotJava和Java平台为公司高层进行演示。1994年,Java 1.0a版本已经可以提供下载,但是Java和HotJava浏览器的第一次公开发布却是在1995年3月23日SunWorld大会上进行的。升阳公司的科学指导约翰·盖吉宣告Java技术。这个发布是与网景公司的执行副总裁马克·安德森的惊人发布一起进行的,宣布网景将在其浏览器中包含对Java的支持。1996年1月,Sun公司成立了Java业务集团,专门开发Java技术。";
Pattern pattern = compile("([A-Za-z0-9]+)");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
System.out.println("找到" + matcher.group(0));
}
}
/**
* 验证:找到文本中所有的IP地址
*/
@Test
public void test2() {
String str = "以下列出留用的内部私有地址\nA类 10.0.0.0--10.255.255.255\nB类 172.16.0.0--172.31.255.255\nC类 192.168.0.0--192.168.255.255";
Pattern pattern = compile("\\d+.\\d+.\\d+.\\d+");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
System.out.println("ip: " + matcher.group());
}
}
}
正则表达式底层实现
语法
字符匹配符
符号 | 说明 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
[ ] | 可接收的字符列表 | [abc] | a、b、c中的任意1个字符 | |
[^] | 不接收的字符串列表 | [^abc] | 除a、b、c之外的任意1个字符,包括数字和特殊符号 | |
- | 连字符,表示范围 | A-Z | 任意A-Z之间的单个字母,等价于[ABC....Z] | |
. | 匹配除\n (换行)以外的任何字符 | a..b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 | abcb |
\\d | 匹配单个数字字符 == [0-9] | \\d{3}(\\d)? | 3个或4个数字的字符串 | 123、4847 |
\\D | 对 \\d 取反 | \\D(\\d)* | 以单个非数字字符开头,后接任意个数字符串 | m234 |
\\w | 匹配单个数字、大小写字母字符、下划线 == [0-9a-zA-Z_] | \\d{3}(\\w) | 3个数字开头,后面为4个数字字母字符串 | |
\\W | 对 \\W 取反 | \\W+\\d | 至少1个非数字字母字符开头,2个数字结尾的字符串 | #@~19 |
\\s | 匹配任何空白字符(空格、制表符等) | |||
\\S | 对\\s取反 | |||
(?i)abc ,abc不区分大小写 | ||||
a(?i)bc ,bc不区分大小写 | ||||
a((?i)b)c ,b不区分大小写 |
总结
[] 表示可接受字符列表
[^] 表示不可接受字符列表
- 表示范围
. 表示除\n(换行符)之外的任意字符
==========以下为各种特殊情况的代替符号=============
[0123456789] \\d 数字集合
[^0123456789] \\D 非数字集合
[a-zA-Z0-9_] \\w 常用数字字母集合
[^a-zA-Z0-9_] \\W 非常用数字字母集合
[ ] \\s 空白字符(空格、制表符等)
[^ ] \\S 非空白字符
a(?i)bc bc不区分大小写
a((?i)b)c b不区分大小写
代码示例
package com.xxx.verify.regularExpression;
import cn.hutool.core.util.StrUtil;
import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.*;
/**
* @author computer
*/
public class RegExpressionTest {
/**
* 验证:字符匹配符应用
*/
@Test
public void test4() {
String str = "1aBc2b3AB2bc4@# fe";
//基础用法
verifyReg("[]匹配在列表中的任意一个字符",str,"[2b3]");
verifyReg("[^]匹配不在列表中的任意一个字符",str,"[^@#]");
verifyReg("-表示范围",str,"[a-z]");
verifyReg(". 匹配除了\\n的任意一个字符 ",str,".");
//简写匹配数字
verifyReg("\\d匹配任意一个数字 = [0-9]",str,"\\d\\d");
verifyReg("\\D匹配任意一个非数字 = [^0-9]",str,"\\D");
//简写匹配常用字母数字
verifyReg("\\w匹配常用字母数字 的集合中任意一个字符 = [a-zA-Z0-9_]",str,"\\w");
verifyReg("\\W对上面取反 = [^a-zA-Z0-9_]",str,"\\W");
//匹配任意空白字符(空格、制表符等)
verifyReg("\\s匹配任意空白字符",str,"\\s");
verifyReg("\\S匹配任意非空白字符",str,"\\S");
//不区分字母大小写进行匹配
verifyReg("(?i)abc表示abc不区分大小写",str,"(?i)abc");
verifyReg("a(?i)bc表示bc不区分大小写",str,"a(?i)bc");
verifyReg("a((?i)b)c表示b不区分大小写",str,"a((?i)b)c");
//不区分大小写简略写法
verifyReg("不区分大小写简略写法",str,"abc",true);
}
/**
* 验证正则表达式
* 传入内容和正则表达式,打印出匹配上的内容
* @param content
* @param reg
*/
public void verifyReg(String target, String content,String reg,boolean isSensitive){
Pattern pattern = isSensitive ? compile(reg, CASE_INSENSITIVE) : compile(reg);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
if (StrUtil.isNotBlank(target)){
System.out.println(target + " : " + matcher.group(0));
}else {
System.out.println(matcher.group(0));
}
}
}
public void verifyReg(String target, String content,String reg){
verifyReg(target,content,reg,false);
}
}
限定符
用于指定其前面的字符和组合项连续出现多少次
符号 | 说明 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
* | 指定字符重复0次或n次(任意次数) [0,+∞] | (abc)* | 包含任意个abc的字符串 | |
+ | 指定字符重复1次或n次(至少一次) [1,+∞] | m+(abc)* | m开头,至少一次;后面接任意个abc | mabc |
? | 指定字符重复0次或1次(最多一次) [0,1] | m+abc? | m开头,至少一次;后面ab/abc | mmabc |
只能输入n个字符 | [abcd] | 由abc组成的任意长度为3的字符串 | abc/bcd/acd | |
指定至少n个匹配 [n,+∞] | [abc] | 由abc组成的任意长度不小于3的字符串 | ||
指定 [n,m) 范围内的字符数量 | [abcd] | 由abcd组成的任意长度[3,4]的字符串 |
/**
* 验证:限定符
*/
@Test
public void test6() {
//+指定字符出现[1,+∞]次
verifyReg("+指定字符出现[1,+∞]次","abcaabb","a+");
verifyReg("+指定字符出现[1,+∞]次","111111","\\d+");
//?指定字符出现[0,1]次
verifyReg("?指定字符出现[0,1]次","222ffe322d","a-z]+\\d?[");
//*指定字符出现[0,+∞]次
verifyReg("*指定字符出现[0,+∞]次","abcaabb","ab*");
//{n}指定目标字符出现n次
verifyReg("{n}表示指定字符出现次数","111111","1{2}");
verifyReg("{n}表示指定字符出现次数","dabefacbghbcgac","[abc]{2}");
//{n,m}指定目标字符出现[n,m)次,默认为贪婪匹配,即尽可能匹配多的
verifyReg("{n,m}指定目标字符出现[n,m]次","aaa333aaaa2111","a{2,4}");
}
选择匹配符
符号 | 说明 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
| | 匹配"|"之前或之后的表达式 | ab|cd | ab或者cd |
/**
* 验证:选择匹配符
*/
@Test
public void test5() {
String str = "韩寒冷";
verifyReg("|选择匹配任意",str,"韩|寒");
}
定位符
规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置
符号 | 说明 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
^ | 指定起始字符 | ^[0-9]+[a-z]* | 至少1个数字开头,后面接任意个小写字母 | 123、2fefe |
$ | 指定结束字符 | ^\\d\\-[a-z]+$ | 1个数字开头,连接一个-,最后以至少1个小写字母结尾 | 5-23b |
\\b | 匹配目标字符串的边界 | han\\b | 代码空格或者结束,即han后面为空格或者到了结尾 | effhan nnfehan |
\\B | \\b的取反 | han\\B | 代表han后面不能由空格或者是结尾位置 |
/**
* 验证:定位符
*/
@Test
public void test7() {
//^指定起始字符
verifyReg("^指定起始字符","12fef","^[0-9]+[a-z]*");
//$指定结束字符
verifyReg("^指定起始字符,$指定结束字符","1-fe","^\\d\\-[a-z]+$");
//\\b匹配目标字符串的边界
verifyReg("\\\\b匹配目标字符串的边界","efjahan nnfhanejihan","[a-z]?han\\b");
//\\B匹配目标字符串的边界
verifyReg("\\\\B对\\\\b的取反","efjahan nnfhanejihan","[a-z]?han\\B");
}
分组
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获。捕获匹配的子字符串。编号为0的为第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号的顺序从1开始自动编号 |
(?<name>pattern) | 命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?'name') |
(?:pattern) | 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。对于用"or"字符(|)组合模式部件的情况很有用,例如: industr(?:y|ies) 是比 ‘industry|industries’更经济的表达式 |
(?=pattern) | 非捕获匹配,例如 Windows(?=95|98|NT|2000) 匹配 "Windows2000"中的Windows,但是不匹配"Windows7"中的Windows |
(?!pattern) | 对(?=pattern)取反,Windows(?=95|98|NT|2000) 匹配 "Windows7"中的Windows,但是不匹配"Windows2000"中的Windows |
代码示例
/**
* 验证:分组
*/
@Test
public void test8() {
//(pattern) 非命名捕获,这里参考正则表达式原理章节
//1. matcher.group(0) 得到匹配到的字符串
//2. matcher.group(1) 得到匹配到的字符串的第1个分组内容
//3. matcher.group(2) 得到匹配到的字符串的第2个分组内容
Pattern pattern = compile("(\\d\\d)(\\d\\d)");
Matcher matcher = pattern.matcher("3ff1256fe1909f");
while (matcher.find()){
System.out.println("非命名捕获 找到字符串:" + matcher.group(0));
System.out.println("第1个分组内容: " + matcher.group(1));
System.out.println("第2个分组内容: " + matcher.group(2));
}
//(?<name>pattern) 命名捕获
pattern = compile("(?<g1>\\d\\d)(?<g2>\\d\\d)");
matcher = pattern.matcher("3ff1256fe1909f");
while (matcher.find()){
System.out.println("命名捕获 找到字符串:" + matcher.group(0));
System.out.println("第1个分组内容(通过下标): " + matcher.group(1));
System.out.println("第1个分组内容(通过命名): " + matcher.group("g1"));
System.out.println("第2个分组内容(通过下标): " + matcher.group(2));
System.out.println("第2个分组内容(通过命名): " + matcher.group("g2"));
}
//(?:pattern) 非命名捕获,是选择匹配符|的替代写法,()中的表达式参与捕获
//找到windows98或者windowsXP
verifyReg("(?:pattern) 非命名捕获,()中的表达式参与捕获","windows98和windows2000和windowsXP和windows11","windows(?:98|XP)");
verifyReg("(?:pattern) 另一种写法","windows98和windows2000和windowsXP和windows11","windows98|windowsXP");
//(?=pattern) 非命名捕获,()中的表达式不参与捕获
//找到结尾是98|XP的windows字符串
verifyReg("(?=pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?=98|XP)");
//(?!pattern) 非命名捕获,()中的表达式不参与捕获,取反
//找到结尾不是98|XP的windows字符串
verifyReg("(?!pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?!98)");
}
贪婪匹配和非贪婪匹配
/**
* 验证:贪婪匹配与非贪婪匹配
限定符之后加上?,则只匹配最少的内容即停止
*/
@Test
public void test9() {
verifyReg("贪婪匹配","fea11111","a\\d+");
verifyReg("非贪婪匹配","fea11111","a\\d+?");
}
正则实例(Pattern.matchers)
/**
* 验证:应用实例
*/
@Test
public void test10() {
//全部都是汉字
String content = "中华人民共2和国";
String reg = "^[\u4e00-\u9fa5]+$";
verifyIsMatch("汉字",content,reg);
//邮政编码: 1-9开头的一个六位数
content = "362428";
reg = "^[1-9]\\d{5}";
verifyIsMatch("邮政编码",content,reg);
//QQ号: 1-9开头的一个(5位数 - 10位数)
content = "1060987218";
reg = "^[1-9]\\d{4,9}";
verifyIsMatch("QQ号",content,reg);
//手机号: 13、14、15、16、18、19开头的一个11位数)
content = "18170461186";
reg = "^1[3|4|5|6|8|9]\\d{9}";
verifyIsMatch("手机号",content,reg);
//URL
content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver";
//1. ^((http|https)://) 匹配 http:// 或者 https://
//2. ([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
//3. (/[\w-?=&./%#]*)? 匹配 /video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver
reg = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(/[\\w-?=&./%#]*)?$";
verifyIsMatch("URL",content,reg);
//电子邮件:1.只能有一个@ 2. @前面是用户名 a-zA-Z0-9 3. @后面是域名,只能是英文字母,例如sohu.com tinghua.com.cn
content = "102893488@qq.com";
reg = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
verifyIsMatch("邮箱",content,reg);
//整数或者小数,考虑正负
content = "0.2";
reg = "^[+-]?([1-9]+\\d*|0)(\\.\\d+)?$";
verifyIsMatch("整数或者小数",content,reg);
//对URL进行解析,得到1. 协议; 2. 域名; 3. 端口; 4. 资源
content = "http://www.souhu.com:8089/abc/index.html";
reg = "^([a-zA-Z]+)://([a-zA-Z.]+[\\w-]+):(\\d+)[\\w-/]*/([\\w.]+)$";
verifyIsMatch("url",content,reg);
Pattern pattern = compile(reg);
Matcher matcher = pattern.matcher(content);
if (matcher.matches()){
System.out.println("整体匹配成功="+ matcher.group(0));
System.out.println("协议="+ matcher.group(1));
System.out.println("域名="+ matcher.group(2));
System.out.println("端口="+ matcher.group(3));
System.out.println("资源="+ matcher.group(4));
}
}
Matcher类的常用方法
方法列表
方法 | 说明 |
---|---|
matcher.start() | 获取当前匹配到的内容的起始下标 |
matcher.end() | 获取当前匹配到的内容的结束下标 |
matcher.matchers() | 整体匹配,用于验证字符串是否某个规则 |
matcher.replaceAll() | 将匹配中的字符串进行全部替换 |
代码示例
/**
* 验证:Matcher方法
*/
@Test
public void test11() {
String content = "hello abc hello tom hello cat";
String reg = "hello";
Pattern pattern = compile(reg);
Matcher matcher = pattern.matcher(content);
//matcher.start() 获取当前匹配到的内容的起始下标
//matcher.end() 获取当前匹配到的内容的结束下标
while (matcher.find()){
System.out.println("==========");
System.out.println(matcher.start());
System.out.println(matcher.end());
System.out.println("group方法获取: "+matcher.group(0));
System.out.println("通过位置获取" + content.substring(matcher.start(),matcher.end()));
}
//matcher.matches()
//整体匹配,用于验证字符串是否某个规则
pattern = compile("[1-9]\\d{5}");
matcher = pattern.matcher("362428");
System.out.println(matcher.matches() ? "邮政编码格式: YES" : "邮政编码格式: NO");
//matcher.replaceAll()
//将匹配中的字符串进行全部替换
pattern = compile("\\(\\s*?\\S*?\\)");
matcher = pattern.matcher("JAVA是一门动态(即时编译)编程语言(language)");
System.out.println(matcher.replaceAll(""));
}
反向引用
代码示例
/**
* 验证:反向引用
* 圆括号的内容被捕获后,可以在这个括号后被使用,我们称为反向引用;这种引用在正则表达式内外部都可以
* 内部反向引用 \\分组号
* 外部反向引用 $分组号、
*/
@Test
public void test12() {
verifyReg("匹配两个连续的相同数字","213433fe442fjie2133","(\\d)\\1");
verifyReg("匹配五个连续的相同数字","333333211124455443433333222222","(\\d)\\1{4}");
verifyReg("匹配个位与千位相同,十位与百位相同的数字,即ABBA","A3443B5445C3342","(\\d)(\\d)\\2\\1");
verifyReg("前面5位数字,中间-,最后9位数字,每3位相同","fe212132323-333222999fe3d23225","\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}");
//我...我要...学学学学...编程
String content = "我...我要...学学学学...编程";
Pattern pattern = compile("\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
String $1 = compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println($1);
}
String类中直接使用正则表达式
代码示例
/**
* 验证:String类直接支持正则表达式
*/
@Test
public void test13() {
//替换 replaceAll
System.out.println("jdk1.3和jdk1.4发布于98年,其中jdk1.3".replaceAll("jdk1\\.(3|4)", "JDK"));
//匹配 matches
System.out.println(("18176453321".matches("1(3|5|7|8|9)\\d{9}")) ? "手机号码格式正确" : "格式不正确");
//分割 split
String[] split = "hello#fjei-ji2和~河南".split("\\#|\\-|\\~");
for (String s : split) {
System.out.println(s);
}
}
完整代码
package com.pxl.verify.regularExpression;
import cn.hutool.core.util.StrUtil;
import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.*;
/**
* @author computer
*/
public class RegExpressionTest {
/**
* 验证:正则表达式体验 - 找到文本中所有的英文字母和数字
*/
@Test
public void test1() {
String str = "1994年6月,在同约翰·盖吉、詹姆斯·高斯林、比尔·乔伊、帕特里克·诺顿、韦恩·罗斯因和埃里克·施密特经历了一场历时三天的头脑风暴后,团队决定再一次改变努力的目标,这次他们决定将该技术应用于万维网。他们认为随着Mosaic浏览器的到来,因特网正在向同样的高度互动的远景演变,而这一远景正是他们在有线电视网中看到的。作为原型,帕特里克·诺顿写了一个小型万维网浏览器,WebRunner,后来改名为HotJava[13]。\n" +
"\n" +
"1994年10月,HotJava和Java平台为公司高层进行演示。1994年,Java 1.0a版本已经可以提供下载,但是Java和HotJava浏览器的第一次公开发布却是在1995年3月23日SunWorld大会上进行的。升阳公司的科学指导约翰·盖吉宣告Java技术。这个发布是与网景公司的执行副总裁马克·安德森的惊人发布一起进行的,宣布网景将在其浏览器中包含对Java的支持。1996年1月,Sun公司成立了Java业务集团,专门开发Java技术。";
Pattern pattern = compile("([A-Za-z0-9]+)");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
System.out.println("找到" + matcher.group(0));
}
}
/**
* 验证:找到文本中所有的IP地址
*/
@Test
public void test2() {
String str = "以下列出留用的内部私有地址\nA类 10.0.0.0--10.255.255.255\nB类 172.16.0.0--172.31.255.255\nC类 192.168.0.0--192.168.255.255";
Pattern pattern = compile("\\d+.\\d+.\\d+.\\d+");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
System.out.println("ip: " + matcher.group());
}
}
/**
* 验证:正则表达式的转义符
* java中使用两个反斜线对特殊字符(.*+()$/\?[]^{})进行转义
*/
@Test
public void test3() {
String str = "abc$(cdf$(";
Pattern pattern = compile("\\$");
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
/**
* 验证:字符匹配符应用
*/
@Test
public void test4() {
String str = "1aBc2b3AB-2_bc4@# fe";
//基础用法
verifyReg("[]匹配在列表中的任意一个字符",str,"[2b3]");
verifyReg("[^]匹配不在列表中的任意一个字符",str,"[^@#]");
verifyReg("-表示范围",str,"[a-z]");
verifyReg(". 匹配除了\\n的任意一个字符 ",str,".");
//简写匹配数字
verifyReg("\\d匹配任意一个数字 = [0-9]",str,"\\d\\d");
verifyReg("\\D匹配任意一个非数字 = [^0-9]",str,"\\D");
//简写匹配常用字母数字
verifyReg("\\w匹配常用字母数字 的集合中任意一个字符 = [a-zA-Z0-9_]",str,"\\w");
verifyReg("\\W对上面取反 = [^a-zA-Z0-9_]",str,"\\W");
//匹配任意空白字符(空格、制表符等)
verifyReg("\\s匹配任意空白字符",str,"\\s");
verifyReg("\\S匹配任意非空白字符",str,"\\S");
//不区分字母大小写进行匹配
verifyReg("(?i)abc表示abc不区分大小写",str,"(?i)abc");
verifyReg("a(?i)bc表示bc不区分大小写",str,"a(?i)bc");
verifyReg("a((?i)b)c表示b不区分大小写",str,"a((?i)b)c");
//不区分大小写简略写法
verifyReg("不区分大小写简略写法",str,"abc",true);
}
/**
* 验证:选择匹配符
*/
@Test
public void test5() {
String str = "韩寒冷";
verifyReg("|选择匹配",str,"韩|寒");
}
/**
* 验证:限定符
*/
@Test
public void test6() {
//+指定字符出现[1,+∞]次
verifyReg("+指定字符出现[1,+∞]次","abcaabb","a+");
verifyReg("+指定字符出现[1,+∞]次","111111","\\d+");
//?指定字符出现[0,1]次
verifyReg("?指定字符出现[0,1]次","222ffe322d","a-z]+\\d?[");
//*指定字符出现[0,+∞]次
verifyReg("*指定字符出现[0,+∞]次","abcaabb","ab*");
//{n}指定目标字符出现n次
verifyReg("{n}表示指定字符出现次数","111111","1{2}");
verifyReg("{n}表示指定字符出现次数","dabefacbghbcgac","[abc]{2}");
//{n,m}指定目标字符出现[n,m)次,默认为贪婪匹配,即尽可能匹配多的
verifyReg("{n,m}指定目标字符出现[n,m]次","aaa333aaaa2111","a{2,4}");
}
/**
* 验证:定位符
*/
@Test
public void test7() {
//^指定起始字符
verifyReg("^指定起始字符","12fef","^[0-9]+[a-z]*");
//$指定结束字符
verifyReg("^指定起始字符,$指定结束字符","1-fe","^\\d\\-[a-z]+$");
//\\b匹配目标字符串的边界
verifyReg("\\\\b匹配目标字符串的边界","efjahan nnfhanejihan","[a-z]?han\\b");
//\\B匹配目标字符串的边界
verifyReg("\\\\B对\\\\b的取反","efjahan nnfhanejihan","[a-z]?han\\B");
}
/**
* 验证:分组
*/
@Test
public void test8() {
//(pattern) 非命名捕获,这里参考正则表达式原理章节
//1. matcher.group(0) 得到匹配到的字符串
//2. matcher.group(1) 得到匹配到的字符串的第1个分组内容
//3. matcher.group(2) 得到匹配到的字符串的第2个分组内容
Pattern pattern = compile("(\\d\\d)(\\d\\d)");
Matcher matcher = pattern.matcher("3ff1256fe1909f");
while (matcher.find()){
System.out.println("非命名捕获 找到字符串:" + matcher.group(0));
System.out.println("第1个分组内容: " + matcher.group(1));
System.out.println("第2个分组内容: " + matcher.group(2));
}
//(?<name>pattern) 命名捕获
pattern = compile("(?<g1>\\d\\d)(?<g2>\\d\\d)");
matcher = pattern.matcher("3ff1256fe1909f");
while (matcher.find()){
System.out.println("命名捕获 找到字符串:" + matcher.group(0));
System.out.println("第1个分组内容(通过下标): " + matcher.group(1));
System.out.println("第1个分组内容(通过命名): " + matcher.group("g1"));
System.out.println("第2个分组内容(通过下标): " + matcher.group(2));
System.out.println("第2个分组内容(通过命名): " + matcher.group("g2"));
}
//(?:pattern) 非命名捕获,是选择匹配符|的替代写法,()中的表达式参与捕获
//找到windows98或者windowsXP
verifyReg("(?:pattern) 非命名捕获,()中的表达式参与捕获","windows98和windows2000和windowsXP和windows11","windows(?:98|XP)");
verifyReg("(?:pattern) 另一种写法","windows98和windows2000和windowsXP和windows11","windows98|windowsXP");
//(?=pattern) 非命名捕获,()中的表达式不参与捕获
//找到结尾是98|XP的windows字符串
verifyReg("(?=pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?=98|XP)");
//(?!pattern) 非命名捕获,()中的表达式不参与捕获,取反
//找到结尾不是98|XP的windows字符串
verifyReg("(?!pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?!98)");
}
/**
* 验证:贪婪匹配与非贪婪匹配
*/
@Test
public void test9() {
verifyReg("贪婪匹配","fea11111","a\\d+");
verifyReg("非贪婪匹配","fea11111","a\\d+?");
}
/**
* 验证:应用实例
*/
@Test
public void test10() {
//全部都是汉字
String content = "中华人民共2和国";
String reg = "^[\u4e00-\u9fa5]+$";
verifyIsMatch("汉字",content,reg);
//邮政编码: 1-9开头的一个六位数
content = "362428";
reg = "^[1-9]\\d{5}";
verifyIsMatch("邮政编码",content,reg);
//QQ号: 1-9开头的一个(5位数 - 10位数)
content = "1060987218";
reg = "^[1-9]\\d{4,9}";
verifyIsMatch("QQ号",content,reg);
//手机号: 13、14、15、16、18、19开头的一个11位数)
content = "18170461186";
reg = "^1[3|4|5|6|8|9]\\d{9}";
verifyIsMatch("手机号",content,reg);
//URL
content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver";
//1. ^((http|https)://) 匹配 http:// 或者 https://
//2. ([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
//3. (/[\w-?=&./%#]*)? 匹配 /video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver
reg = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(/[\\w-?=&./%#]*)?$";
verifyIsMatch("URL",content,reg);
//电子邮件:1.只能有一个@ 2. @前面是用户名 a-zA-Z0-9 3. @后面是域名,只能是英文字母,例如sohu.com tinghua.com.cn
content = "102893488@qq.com";
reg = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
verifyIsMatch("邮箱",content,reg);
//整数或者小数,考虑正负
content = "0.2";
reg = "^[+-]?([1-9]+\\d*|0)(\\.\\d+)?$";
verifyIsMatch("整数或者小数",content,reg);
//对URL进行解析,得到1. 协议; 2. 域名; 3. 端口; 4. 资源
content = "http://www.souhu.com:8089/abc/index.html";
reg = "^([a-zA-Z]+)://([a-zA-Z.]+[\\w-]+):(\\d+)[\\w-/]*/([\\w.]+)$";
verifyIsMatch("url",content,reg);
Pattern pattern = compile(reg);
Matcher matcher = pattern.matcher(content);
if (matcher.matches()){
System.out.println("整体匹配成功="+ matcher.group(0));
System.out.println("协议="+ matcher.group(1));
System.out.println("域名="+ matcher.group(2));
System.out.println("端口="+ matcher.group(3));
System.out.println("资源="+ matcher.group(4));
}
}
/**
* 验证:Matcher方法
*/
@Test
public void test11() {
String content = "hello abc hello tom hello cat";
String reg = "hello";
Pattern pattern = compile(reg);
Matcher matcher = pattern.matcher(content);
//matcher.start() 获取当前匹配到的内容的起始下标
//matcher.end() 获取当前匹配到的内容的结束下标
while (matcher.find()){
System.out.println("==========");
System.out.println(matcher.start());
System.out.println(matcher.end());
System.out.println("group方法获取: "+matcher.group(0));
System.out.println("通过位置获取" + content.substring(matcher.start(),matcher.end()));
}
//matcher.matches()
//整体匹配,用于验证字符串是否某个规则
pattern = compile("[1-9]\\d{5}");
matcher = pattern.matcher("362428");
System.out.println(matcher.matches() ? "邮政编码格式: YES" : "邮政编码格式: NO");
//matcher.replaceAll()
//将匹配中的字符串进行全部替换
pattern = compile("\\(\\s*?\\S*?\\)");
matcher = pattern.matcher("JAVA是一门动态(即时编译)编程语言(language)");
System.out.println(matcher.replaceAll(""));
}
/**
* 验证:反向引用
* 圆括号的内容被捕获后,可以在这个括号后被使用,我们称为反向引用;这种引用在正则表达式内外部都可以
* 内部反向引用 \\分组号
* 外部反向引用 $分组号、
*/
@Test
public void test12() {
verifyReg("匹配两个连续的相同数字","213433fe442fjie2133","(\\d)\\1");
verifyReg("匹配五个连续的相同数字","333333211124455443433333222222","(\\d)\\1{4}");
verifyReg("匹配个位与千位相同,十位与百位相同的数字,即ABBA","A3443B5445C3342","(\\d)(\\d)\\2\\1");
verifyReg("前面5位数字,中间-,最后9位数字,每3位相同","fe212132323-333222999fe3d23225","\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}");
//我...我要...学学学学...编程
String content = "我...我要...学学学学...编程";
Pattern pattern = compile("\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
String $1 = compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println($1);
}
/**
* 验证:String类直接支持正则表达式
*/
@Test
public void test13() {
//替换 replaceAll
System.out.println("jdk1.3和jdk1.4发布于98年,其中jdk1.3".replaceAll("jdk1\\.(3|4)", "JDK"));
//匹配 matches
System.out.println(("18176453321".matches("1(3|5|7|8|9)\\d{9}")) ? "手机号码格式正确" : "格式不正确");
//分割 split
String[] split = "hello#fjei-ji2和~河南".split("\\#|\\-|\\~");
for (String s : split) {
System.out.println(s);
}
}
public void verifyIsMatch(String target,String content,String reg){
System.out.println(Pattern.matches(reg,content) ? target +",整体匹配成功" : target +",整体匹配失败");
}
/**
* 验证正则表达式
* 传入内容和正则表达式,打印出匹配上的内容
* @param content
* @param reg
*/
public void verifyReg(String target, String content,String reg,boolean isSensitive){
Pattern pattern = isSensitive ? compile(reg, CASE_INSENSITIVE) : compile(reg);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
if (StrUtil.isNotBlank(target)){
System.out.println(target + " : " + matcher.group(0));
}else {
System.out.println(matcher.group(0));
}
}
}
public void verifyReg(String target, String content,String reg){
verifyReg(target,content,reg,false);
}
}
本文来自博客园,作者:江南西道,转载请注明原文链接:https://www.cnblogs.com/jiangnanxidao/p/15734618.html