文章目录
- 1 正则表达式基本介绍
- 2 正则表达式语法
- 2.1 元字符-转义号 \\\
- 2.2 元字符-字符匹配符
- 2.3 元字符-选择匹配符
- 2.4 元字符-限定符
- 2.5 元字符-定位符
- 2.6 分组
- 3 正则表达式三个常用类
- 3.1 Pattern类的Matches方法
- 3.2 Matcher类的常用方法
- 4 分组、捕获、反向引用
- 5 String类中使用正则表达式
- 5.1 替换功能
- 5.2 判断功能
- 5.3 分割功能
- 6 正则表达式使用案例
- 6.1 验证电子邮箱格式是否合法
- 6.2 验证手机号码是否合法
- 6.3 验证URL格式是否合法
- 7 正则表达式底层实现
- 8 Reference
1 正则表达式基本介绍
正则表达式(Regular Expression),简单地说:正则表达式是对字符串执行模式匹配的技术。
一个正则表达式,就是用某种模式去匹配字符串的一个公式。
2 正则表达式语法
如果要想灵活的运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为:
- 限定符
- 选择匹配符
- 分组组合和反向引用符
- 特殊字符
- 字符匹配符
- 定位符
2.1 元字符-转义号 \\
在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错的。
需要用到转义符号的字符有以下:
. * + () $ / \ ? [ ] ^ { }
2.2 元字符-字符匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
[ ] | 可接收字符列表 | [abcd] | a、b、c、d中任意1个字符 | a,b,c,d |
[^] | 不接收字符列表 | [^abc] | 除a、b、c之外的任意一个字符 | d,1,#,… |
- | 连字符 | a-z | 任意单个小写字母 | a,d,… |
. | 匹配除\n以外的的任何字符 | a…b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 | a12b,aw@b,… |
\\d | 匹配单个数字字符,相当于[0,9] | \\d{3}(\\d)? | 包含3个或4个数字的字符串 | 123,1234,… |
\\D | 匹配单个非数字字符,相当于[ ^0-9] | \\D(\\d)* | 以单个非数字字符开头,后接任意个数字字符串 | q,#,A123,… |
\\w | 匹配单个数字、大小写字母、下划线,相当于[0-9a-zA-Z_] | \\d{3}\\w{4} | 以3个数字开头的长度为7的数字字母字符串 | 123abcd,12345ab,1234567 |
\\W | 匹配单个非数字、大小写字母、下划线字符,相当于[ ^0-9a-zA-Z] | \\W+\\d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串 | #12,#*&@90,… |
\\s | 匹配任意空白字符(空格,制表符) | \\s | ||
\\S | 匹配任意非空白字符(空格,制表符) | \\S |
2.3 元字符-选择匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
| | 匹配”|“之前或之后的表达式 | ab|cd | ab或者cd | ab,cd |
2.4 元字符-限定符
用于指定前面的字符和组合项连续出现多少次
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
* | 指定字符重复0次或n次(0到多) | (abc)* | 仅包含任意个abc的字符串 | abc,abcabc,… |
+ | 指定字符重复1次或n次(1到多) | A+(ab)* | 以至少1个A开头,后接任意个ab的字符串 | Aab,AAabab |
? | 指定字符重复0次或1次(0到1) | A+abc? | 以至少1个A开头,后接ab或abc的字符串 | Aab,AAabc |
{n} | 只能输入n个字符 | [abcd]{3} | 由abcd中字母组成任意长度为3的字符串 | abc,acd,… |
{n,} | 指定至少n个匹配 | [abcd]{3,} | 由abcd中字母组成任意长度不小于3的字符串 | aab,abcd,aabbcd |
{n,m} | 指定至少n个但不多于m个匹配 | [abcd]{3,5} | 由abcd中字母组成任意长度不小于3,不大于5的字符串 | abc,abcd,aaaaa |
2.5 元字符-定位符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
^ | 指定起始字符 | ^ [0-9]+[a-z]* | 以至少1个数字开头,后接任意个小写字母的字符串 | 123,1aa,22esf |
$ | 指定结束字符 | ^ [0-9]\\-[a-z]+$ | 以1个数字开头后接连字符”-“,并以至少1个小写字母结尾的字符串 | 1-a,1-abc |
\\b | 匹配目标字符串的边界 | abc\\b | 这里的字符串的边界指的是子串间有空格,或者是目标字符串的结束位置 | qwerabc |
\\B | 匹配目标字符串的非边界 | abc\\B | 和\b的含义相反 | abcqwer |
2.6 分组
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获。捕获匹配的子字符串。编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号 |
(?pattern) | 命名捕获。将匹配的字符串捕获到一个组名或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?’name‘) |
(?:pattern) | 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储以后使用的匹配。这对于用”or“字符(|)组合模式部件的情况很有用。例如,’industr(?:y|ies)‘是比’industry|industries‘更经济的表达式 |
(?=pattern) | 它是一个非捕获匹配。例如’Windows(?=95|98|NT|2000)匹配”Windows 2000“中的”Windows“,但不匹配”Windows 10“中的”Windows“ |
(?!pattern) | 该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如’Windows(?=95|98|NT|2000)匹配”Windows 10“中的”Windows“,但不匹配”Windows 2000“中的”Windows“ |
package regularexpression;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @package: regularexpression* @description:分组* @author: Yunyang* @date: 2023/11/26 20:52* @version:1.0**/
public class RegExp07 {public static void main(String[] args) {String content = "helloworld s7789 n1189han";// String regStr = "(\\d\\d)(\\d\\d)";//匹配4个数字的字符串// String regStr = "(\\d\\d)(\\d)(\\d)";//匹配4个数字的字符串String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";//匹配4个数字的字符串Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);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"));// System.out.println("找到第3个分组:"+matcher.group(3));}}
}
3 正则表达式三个常用类
java.util.regex包主要包括以下三个类:Pattern类、Matcher类和PatternSyntaxException类
- Pattern类
pattern对象是一个正则表达式对象。Pattern类没有公共构造方法。要创建一个Pattern对象,调用其公共静态方法,它返回一个Pattern对象。
- Matcher类
Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern一样,Matcher也没有公告构造方法。需要调用Pattern对象的matcher方法来获得一个Matcher对象。
- PatternSyntaxException类
PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式下的语法错误。
3.1 Pattern类的Matches方法
package regularexpression;import java.util.regex.Pattern;/*** @package: regularexpression* @description:matches方法,用于整体匹配,在研制输入的字符串是否满足条件使用* @author: Yunyang* @date: 2023/11/26 22:03* @version:1.0**/
public class PatternMethod {public static void main(String[] args) {String content = "hello world hello java,你好世界";String regStr = "hello.*";boolean matches = Pattern.matches(regStr, content);System.out.println("整体匹配 = " + matches);}
}
3.2 Matcher类的常用方法
package regularexpression;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @package: regularexpression* @description:Matcher类的常用的方法* @author: Yunyang* @date: 2023/11/26 22:10* @version:1.0**/
public class MatcherMethod {public static void main(String[] args) {String content = "hello world jack hello tom hello smitch hello";String regStr = "hello.*";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("====================");System.out.println(matcher.start());System.out.println(matcher.end());System.out.println("找到:"+content.substring(matcher.start(), matcher.end()));}//整体匹配,常用于校验某个字符串是否满足某个规则System.out.println(matcher.matches());regStr = "world";pattern = Pattern.compile(regStr);matcher = pattern.matcher(content);String newcontent = matcher.replaceAll("世界");//注意:返回的字符串才是替换后的字符串 原来的 content 不变化System.out.println("newcontent = " + newcontent);System.out.println("content = " + content);}
}
4 分组、捕获、反向引用
- 分组
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分就可以看作是一个子表达式/一个分组
例如:(\\d\\d)(\\d\\d)中有两个分组
- 捕获
把正则表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左到右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0表示的是整个正则表达式。
例如:(\\d\\d)(\\d\\d)(\\d)中有三个分组,组0表示整个表达式:(\\d\\d)(\\d\\d),组1表示前一个圆括号的内容(\\d\\d),组2表示第二个括号的内容(\\d\\d),组3表示最后一个括号的内容
- 反向引用
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,称之为反向引用,这种引用既可以是正则表达式内部,也可以是在正则表达式外部,内部反向引用**\\分组号**,外部反向引用**$分组号**
例如:匹配5个连续的相同数字:(\\d)\\1{4}
5 String类中使用正则表达式
5.1 替换功能
String 类 public String replaceAll(String regex,String replacement)
5.2 判断功能
String 类 public boolean matches(String regex){} //使用 Pattern 和 Matcher 类
5.3 分割功能
String 类 public String[] split(String regex)
package regularexpression;/*** @package: regularexpression* @description:String类中使用正则表达式* @author: Yunyang* @date: 2023/11/26 23:08* @version:1.0**/public class StringReg {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及可变参数。";//使用正则表达式将JDK1.3和JDK1.4替换成JDKcontent = content.replaceAll("JDK1\\.3|JDK1\\.4","JDK");System.out.println(content);//要求 验证一个 手机号, 要求必须是以 138 139 开头的content = "13988889999";if (content.matches("13(8|9)\\d{8}")) {System.out.println("验证成功");} else {System.out.println("验证失败");}//要求按照 # 或者 - 或者 ~ 或者 数字 来分割System.out.println("===================");content = "hello#abc-jack12smith~北京";String[] split = content.split("#|-|~|\\d+");for (String s : split) {System.out.println("s = " + s);}}
}
6 正则表达式使用案例
6.1 验证电子邮箱格式是否合法
package regularexpression;/*** @package: regularexpression* @description:验证电子邮件格式是否合法* @author: Yunyang* @date: 2023/12/3 21:31* @version:1.0**/
public class CheckEmail {public static void main(String[] args) {// String content = "hsp@shun.cn.com";String content = "2834660387@qq.com.cn";String regStr = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";// String 的matches是整体匹配if(content.matches(regStr)){System.out.println("匹配成功");} else {System.out.println("匹配失败");}}
}
6.2 验证手机号码是否合法
要求: 必须以 13,14,15,18 开头的 11 位数 , 比如 13588889999
package regularexpression;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @package: regularexpression* @description:验证手机号码是否合法* @author: Yunyang* @date: 2023/11/26 21:20* @version:1.0**/
public class CheckPhone {public static void main(String[] args) {// 要求: 必须以 13,14,15,18 开头的 11 位数 , 比如 13588889999String content = "13588889999";String regStr = "^1[3|4|5|8]\\d{9}$";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);if(matcher.find()){System.out.println("满足格式");} else {System.out.println("不满足格式");}System.out.println(Pattern.matches(regStr, content));}
}
6.3 验证URL格式是否合法
package regularexpression;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @package: regularexpression* @description:正则表达式的应用:url* @author: Yunyang* @date: 2023/11/26 21:42* @version:1.0**/
public class RegExp11 {public static void main(String[] args) {// String content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver";String content = "https://docs.qq.com/doc/DR1NvRkNFU2ZpWGxQ?u=fc936b78ceee42de9e76d148895f84f3";/** 思路:* 1.先确定url的开始部分 http | https://* 2.然后通过([\w-]+\.)+[\w-]+ 匹配www.bilibili.com* 3.3. /video/BV1fh411y7R8?from=sear 匹配(\/[\w-?=&/%.#]*)?** */String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";//注意:[. ? *]表示匹配就是.本身Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);if(matcher.find()){System.out.println("满足格式");} else {System.out.println("不满足格式");}}
}
7 正则表达式底层实现
package regularexpression;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @package: regularexpression* @description:分析Java正则表达式的底层实现* @author: Yunyang* @date: 2023/11/26 17:28* @version:1.0**/
public class RegTheory {public static void main(String[] args) {String content = "1998年12月8日,第二代Java平台的企业版J2EE发布。1999年6月,Sun公司发布了第二代Java平台(简称为Java2)的3个版本:J2ME(Java2 Micro Edition,Java2平台的微型版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企业版),应用于基于Java的应用服务器。Java 2平台的发布,是Java发展过程中最重3443要的一个里程碑,标志着Java的应用开始普及9889。";//匹配所有四个数字//1.\\d表示一个数字String regStr = "(\\d\\d)(\\d\\d)";//2.创建正则表达式对象Pattern pattern = Pattern.compile(regStr);//3.创建匹配器Matcher matcher = pattern.matcher(content);//4.开始匹配/**matcher.find()完成的任务 (考虑分组):* 什么是分组,比如 (\d\d)(\d\d) ,正则表达式中有() 表示分组,第 1 个()表示第 1 组,第 2 个()表示第 2 组...*1.根据指定的规则,定位满足规则的子字符串(比如1998)*2.找到后,将 子字符串的开始的索引记录到 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 的值即 35, 即下次执行 find 时,就从 35 开始匹配*** 源码:* 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();* }从代码中可以看出,数组中的每个分组的信息由两个连续的元素表示:一个是分组的开始位置,另一个是结束位置。因此,对于给定 的 group,它的开始和结束位置分别是 groups[group*2] 和 groups[group*2+1],检查发现开始或结束位置为-1,则返 回 null,表示该分组在匹配的字符串中不存在** 1.根据group[0]=31 和groups[1]=35的记录的位置,从 content 开始截取子字符串返回* 就是 [31,35) 包含 31 但是不包含索引为 35 的位置** 如果再次指向 find 方法.仍然按照上面分析来执行*/while (matcher.find()) {//小结//1. 如果正则表达式有() 即分组//2. 取出匹配的字符串规则如下//3. group(0) 表示匹配到的子字符串//4. group(1) 表示匹配到的子字符串的第一组字串//5. group(2) 表示匹配到的子字符串的第 2 组字串//6. ... 但是分组的数不能越界.System.out.println("找到 " + matcher.group(0));System.out.println("第1组()匹配到的值=" + matcher.group(1));System.out.println("第2组()匹配到的值=" + matcher.group(2));}}
}
8 Reference
-
【韩顺平讲Java】Java 正则表达式专题 -正则 正则表达式 元字符 限定符 Pattern Matcher 分组 捕获 反向引用等
-
菜鸟教程:Java正则表达式
-
《正则表达式必知必会》 作者: [美] Ben Forta 著 , 杨涛 译
-
正则表达式在线工具