regular expression:正则表达式,简写为RegExp,擅长用在文本格式匹配领域
1、正则表达式的元字符
1、转义字符"\"
在java中,用\\来表示转义字符,需要用到转义字符的符号有如下几个:==.*+()$/?[]^{}==但当这些符号在[]时,不需要转义。
2、字符匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
[] | 可以接收的字符列表 | [abcde] | a、b、c、d、e中的任意一个字符 | |
[^] | 不接收的字符列表 | [^abcde] | 除a、b、c、d、e外的任意一个字符,包含特殊字符 | |
- | 连字符 | A-Z | 从A到Z之间的任意一个大写字母 | |
. | 匹配除\n以外的所有字符 | a..b | 以a开头,b结尾,中间包括两个任意字符的总长我4的字符串 | |
\\d | 匹配单个数字字符,相当于[0-9] | \\d{3}(\\d)? | 包括3个或4个数字的字符串 | 123、5678 |
\\D | 匹配单个非数字字符,相当于[^0-9] | \\D(\\d)* | 以当个非数字字符开头,后接任意个数字字符串 | a、A123 |
\\w | 匹配单个数字、大小写字符、,相当于[0-9a-zA-Z] | \\d{3}\\w{4} | 以3个数字开头,后接数字或字母的长度为7的字符串 | 2351234、321hoh2 |
\\W | 匹配单个非数字、大小写字母字符、,相当于[^0-9a-zA-Z] | \W+\d{2} | 以至少一个非数字、大小写字母字符开头,以两个数字字符结尾的字符串 | *23、#33@?10 |
(?i) | 其后锁接的字符串不区分大小写 | (?i)abc | 以abc为顺序的不区分大小写的字符串 | AbC、abC、ABC、abc |
(?i) | 其后锁接的字符串不区分大小写 | a((?i)b)c | 以a开头,以c结尾,中间的b不区分大小写的字符串 | 只有两个,abc、aBc |
\\s | 任何空白字符(空格、制表符等) | |||
\\S | 除去任何空白字符(空格、制表符等),与\\s相反 |
说明:例如“abc”这种不带有表达式的匹配为全匹配,默认情况下,匹配是区分大小写的,解决不区分大小写的问题,除了用表达式实现之外,我们还可以在创建Pattern对象时指定,例如:Patternpattern=Pattern.compile(regStr,Pattern.CASE_INSENSITIVE*/);
3、选择匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
| | |两侧的表达式存在或的关系 | ab|bc | 要么匹配ab,要么匹配bc |
4、限定符
用于指定其前面的字符或组合项连续出现多少次
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
* | 指定前面的字符重复0次或n次 | (abc)* | 包含连续任意个abc字符串 | abcabc、abc |
+ | 指定前面字符串重复1次或n次(至少1次) | m+(abc)* | m出现至少一次,abc可以重复任意次 | m、mabc、mabcabc、mmabcabc |
? | 指定前面的字符出现0次或1次 | m+abc? | 至少以1个m开头,后面接ab或abc的字符串 | mab、mabc、mmmmab、mmabc |
{n} | 只能输入n个字符 | [abcd]{3} | 由a、b、c、d中任意组成的3个长度字符串 | aaa、acd、bbd |
{n,} | 指定至少n个匹配 | [abcd]{3,} | 包含由a、b、c、d组成的,长度至少为3的字符串 | abc、abdc、aabbccdd |
{n,m} | 指定前面的组合至少出现n次,但不多余m次 | [abc]{2,3} | a、b、c三个字符中任意组合成至少2个长度,最多3个长度的字符串 | aa、aaa、abc、cb |
注意,java的匹配是贪婪匹配,当有符合最长的匹配时,会优先匹配最长的,如果要使用非贪婪匹配,在限定符的后面加上?,即可让匹配规则变为非贪婪匹配。例如:a+?来匹配字符串aaaahoih1234hioh,会返回4个a,而不是1个aaaa。
5、定位符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
^ | 指定起始字符 | ^[0-9]+[a-z]* | 以至少一个数字开头,后接任意个小写字母的字符串 | 1、12345、1aaaa |
$ | 指定结束字符 | ^[0-9]\\-[a-z]+$ | 以一个数字开头,加上-,再以一个或多个小写字母结尾的字符串 | 1-a、2-abcd |
\\b | 匹配目标字符串的边界 | abc\\b | 这里说的字符串的边界是指子串间有空格,或者是目标字符串的结束位置 | abcnnabc hoghaoabc |
\\B | 匹配目标字符串的非边界 | abc\\B | 这里说的字符串的边界是指子串间有空格,或者是目标字符串的结束位置 | abcnnabc hoghaoabc |
2、分组
捕获分组
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获。捕获匹配的字符串,编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号的顺序从1开始自动编号 |
(?pattern) | 命名捕获。将匹配的字符串捕获到一个组名称或者编号名称中,用于name的字符串不能包含任何标点符号,并且不能以数字开头,可以使用单引号替代尖括号,例如(?'name’pattern) |
说明:使用了命名分组,可以使用编号来捕获组,也可以用name来捕获组
非捕获分组
3、常用正则表达式
要求 | 规则 | 表达式 |
---|---|---|
汉字字符串 | 用16制编码范围来表示 | ^[\u0391-\uffe5]+$ |
邮政编码 | 以1-9开头的六位数字 | ^[1-9]\\d{5}$ |
QQ号 | 以1-9开头的5-10位数字 | ^[1-9]\\d{4,9}$ |
URL | "^((http | |
电子邮箱 | 只能有一个@,以a-zA-Z0-9-_开头,以com、cn等结尾 | ^[\\w-]+@([a-zA-Z]\.)+[a-zA-Z]+$ |
一、校验数字的表达式
1 数字:1$
2 n位的数字:^\d{n}$
3 至少n位的数字:^\d{n,}$
4 m-n位的数字:^\d{m,n}$
5 零和非零开头的数字:^(0|[1-9][0-9])$
6 非零开头的最多带两位小数的数字:^([1-9][0-9])+(.[0-9]{1,2})?$
7 带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})?$
8 正数、负数、和小数:^(-|+)?\d+(.\d+)?$
9 有两位小数的正实数:2+(.[0-9]{2})?$
10 有1~3位小数的正实数:3+(.[0-9]{1,3})?$
11 非零的正整数:4\d$ 或 ^([1-9][0-9]){1,3}$ 或 ^+?[1-9][0-9]$
12 非零的负整数:^-[1-9][]0-9"$ 或 ^-[1-9]\d$
13 非负整数:^\d+$ 或 5\d*|0$
14 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15 非负浮点数:^\d+(.\d+)?$ 或 6\d*.\d*|0.\d*[1-9]\d*|0?.0+|0$
16 非正浮点数:^((-\d+(.\d+)?)|(0+(.0+)?))$ 或 ^(-([1-9]\d*.\d*|0.\d*[1-9]\d*))|0?.0+|0$
17 正浮点数:7\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]))$
18 负浮点数:^-([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])))$
19 浮点数:^(-?\d+)(.\d+)?$ 或 ^-?([1-9]\d*.\d*|0.\d*[1-9]\d*|0?.0+|0)$
二、校验字符的表达式
1 汉字:8{0,}$
2 英文和数字:9+$ 或 10{4,40}$
3 长度为3-20的所有字符:^.{3,20}$
4 由26个英文字母组成的字符串:11+$
5 由26个大写英文字母组成的字符串:12+$
6 由26个小写英文字母组成的字符串:13+$
7 由数字和26个英文字母组成的字符串:14+$
8 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9 中文、英文、数字包括下划线:15+$
10 中文、英文、数字但不包括下划线等符号:16+$ 或 17{2,20}$
11 可以输入含有^%&’,;=?KaTeX parse error: Can't use function '\"' in math mode at position 1: \̲"̲等字符:[^%&',;=?\x22]+
12 禁止输入含有的字符:[^\x22]+
三、特殊需求表达式
1 Email地址:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)$
2 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3 InternetURL:[a-zA-z]+://[^\s] 或 ^https://([\w-]+.)+[\w-]+(/[\w-./?%&=])?$
4 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
5 电话号码(“XXX-XXXXXXX”、“XXXX-XXXXXXXX”、“XXX-XXXXXXX”、“XXX-XXXXXXXX”、"XXXXXXX"和"XXXXXXXX):^((\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7 身份证号:
15或18位身份证:^\d{15}|\d{18}$
15位身份证:18\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$
18位身份证:19\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{4}$
8 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
9 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):20[a-zA-Z0-9_]{4,15}$
10 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):21\w{5,17}$
11 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$
12 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
14 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15 钱的输入格式:
16 1.有四种钱的表示形式我们可以接受:“10000.00” 和 “10,000.00”, 和没有 “分” 的 “10000” 和 “10,000”:22[0-9]$
17 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9])$
18 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9])$
19 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:23+(.[0-9]+)?$
20 5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 “10” 和 “10.2” 是通过的:24+(.[0-9]{2})?$
21 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:25+(.[0-9]{1,2})?$
22 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:26{1,3}(,[0-9]{3})(.[0-9]{1,2})?$
23 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3}))(.[0-9]{1,2})?$
24 备注:这就是最终结果了,别忘了"+“可以用”"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
25 xml文件:^([a-zA-Z]±?)+[a-zA-Z0-9]+\.[x|X][m|M][l|L]$
26 中文字符的正则表达式:[\u4e00-\u9fa5]
27 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
28 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
29 HTML标记的正则表达式:<(\S*?)[^>]>.?|<.? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
30 首尾空白字符的正则表达式:^\s|\s*KaTeX parse error: Undefined control sequence: \s at position 4: 或(^\̲s̲*)|(\s*) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
31 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
32 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
33 IP地址:\d+.\d+.\d+.\d+ (提取IP地址时有用)
4、正则表达式的三个常用类
1、Pattern类
pattern对象是一个正则表达式对象。Pattern没有公共的构造方法,要创建一个pattern对象,调用其静态方法,它返回一个pattern对象,该方法接收一个正则表达式作为它的第一个参数,例如:Pattern pattern=Pattern.compile(“a+”);
matches方法:整体匹配
@Test
public void testMaches(){
String content ="hello world;hello Beijing";
//String regStr="hello";
String regStr="hello.*";
//如果匹配,就返回true,否则返回false
boolean matches = Pattern.matches(regStr, content);
System.out.println("返回结果为:"+matches);
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210606095119525.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0ODM1MTIw,size_16,color_FFFFFF,t_70)源码及分析
```java
@Test
public void compileAndFind(){
String content="1998年12月8日,第二代Java平台的企业版J2EE发布。1999年6月,Sun公司发布了"+
"第二代Java平台(简称为Java2)的3个版本:J2ME(Java2MicroEdition,Java2平台的微型"+
"版),应用于移动、无线及有限资源的环境;J2SE(Java2StandardEdition,Java2平台的"+
"标准版),应用于桌面环境;J2EE(Java2EnterpriseEdition,Java2平台的企业版),应"+
"用3443于基于Java的应用服务器。Java2平台的发布,是Java发展过程中最重要的一个"+
"里程碑,标志着Java的应用开始普及9889";
//目标:匹配所有四个数字//说明//
// 1.\\d表示一个任意的数字
String regStr="(\\d\\d)(\\d\\d)";
// 2.创建模式对象[即正则表达式对象]
Pattern pattern=Pattern.compile(regStr);
// 3.创建匹配器//说明:创建匹配器matcher,按照正则表达式的规则去匹配content字符串
Matcher matcher=pattern.matcher(content);
//4.开始匹配/****matcher.find()完成的任务(考虑分组)
/*什么是分组,比如(\d\d)(\d\d),正则表达式中有()表示分组,第1个()表示第1组,第2个()表示第2组...
* *1.根据指定的规则,定位满足规则的子字符串(比如(19)(98))
* *2.找到后,将子字符串的开始的索引记录到matcher对象的属性int[]groups;
* 2.1groups[0]=0,把该子字符串的结束的索引+1的值记录到groups[1]=4
* 2.2记录1组()匹配到的字符串groups[2]=0groups[3]=2
* 2.3记录2组()匹配到的字符串groups[4]=2groups[5]=4
* 2.4.如果有更多的分组.....*3.同时记录oldLast的值为子字符串的结束的索引+1的值即35,即下次执行find时,就从35开始匹配**matcher.group(0)分析**源码:
* publicStringgroup(intgroup){
* if(first<0)
* thrownewIllegalStateException("Nomatchfound");
* if(group<0||group>groupCount())
* thrownewIndexOutOfBoundsException("Nogroup"+group);
* if((groups[group*2]==-1)||(groups[group*2+1]==-1))
* return null;
* return getSubSequence(groups[group*2],groups[group*2+1]).toString();*}
* *1.根据groups[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));
}
}
2、Matcher类
Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern类一样,Matcher也没有公共的构造方法,需要调用Pattern类的对象的matcher方法来获取一个Matcher对象
start与end
@Test
public void matcher(){
String regStr="hello";
String content="hello world,hello Beijing";
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());
}
}
matches
整体匹配,常用于校验某个字符串是否满足某个规则
@Test
public void matcher(){
String regStr="hello.*";
String content="hello world,hello Beijing";
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(matcher.matches());
}
replaceAll
根据正在表达式找到的内容,替换为自己想要的内容。
@Test
public void matcher(){
String regStr="hello";
String content="hello world,hello Beijing,Shanghai 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(matcher.matches());
//不会改变原来的content
String newContent = matcher.replaceAll("你好");
System.out.println("原来的内容为:"+content);
System.out.println("替换后的内容为:"+newContent);
}
3、PatterSytaxException
PatterSytaxException是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
5、分组、捕获、反向引用
给出一段文本,要求找出所有四个数字连在一起的子串,并且这四个数满足,①第一个数与第四个数相同②第二个数与第三个数相同,比如,3223、5665。该如何实现呢?
要解决前面的问题,我们需要了解正则表达式的几个概念:
(1)分组
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看做是一个子表达式/一个分组。
(2)捕获
把正则表达式中分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左往右,以分组的左括号为标志,第一个出现的分组的组编号为1,第二个为2,以此类推。前面说过,0号分组代表整个正则式。
(3)反向引用
圆括号的内容被捕获后,我们可以在这个括号后被引用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是正则表达式外部,语法为:内部反向引用**\\分组号**,外部反向引用**$分组号**。
1、反向引用
@Test
public void regexQuote(){
//反向引用
String contet="122333444455555665656";
//匹配两个连续的数字
// String regStr="(\\d)\\1";
//匹配连续五个相同的数字
//String regStr="(\\d)\\1{4}";
//各位与千位相同,十位与百位相同
String regStr="(\\d)(\\d)\\2\\1";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(contet);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
2、结巴去重案例
@Test
public void stammer(){
String content ="我...想想.....要要....学学学学...java";
//1、去掉所有的.
String regStr="[.]";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
String newContent = matcher.replaceAll("");
System.out.println("去除.后的字符串为:"+newContent);
//去掉重复的字
Pattern newPattern = Pattern.compile("(.)\\1+");
Matcher newMatcher = newPattern.matcher(newContent);
while (newMatcher.find()){
System.out.println("找到="+newMatcher.group(0));
}
//使用反向引用来替换
String finalContent = newMatcher.replaceAll("$1");
System.out.println("我不再结巴了:"+finalContent);
}
3、替换分割匹配
String类中使用正则表达式
@Test
public void regString(){
String content="2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其"+
"获得了Apple公司MacOSX的工业标准的支持。2001年9月24日,J2EE1.3发"+
"布。"+"2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升";
//使用正则表达式方式,将JDK1.3和JDK1.4替换成JDK
content=content.replaceAll("JDK1\\.3|JDK1\\.4","JDK");
System.out.println(content);
//要求验证一个手机号,要求必须是以138139开头的
content="13888889999";
if(content.matches("1(38|39)\\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);}
}