参考文献:
https://www.bilibili.com/video/BV1Eq4y1E79W
一. 为什么要学习正则表达式
1.提取文章中的所有英文单词
2.提取文章中的所有数字
3.提取微博热搜的所有标题
4.检测用户的输入(邮箱,电话号码)是否正确
为了解决上述的问题,Java提供了正则表达式技术,专门用来处理类似的文本问题。简单来说,正则表达式是对字符串执行模式匹配技术。
二. 正则表达式语法
1.元字符-转意字符 \\
\\ :在使用正则表达式检索某一些特殊字符的时候,需要用到特殊字符,例如检索"asd("时就需要使用转义字符,否则检索不到结果,且在正则表达式中两个 \\ 代表其他语言中的一个 \。
需要用到转义字符的有如:.*+()$/?[]^{}
2. 元字符-字符匹配符
表1:
符号 | 符号 | 示例 | 解释 |
---|---|---|---|
[ ] | 可以接收的字符列表 | [abcd] | a,b,c,d中任意一个字符 |
^ | 不可接收的字符列表 | [^abcd] | 除a,b,c,d外的任意一个字符,包括数字,特殊字符,英文字母 |
- | 连字符 | [a-z] | 任意单个小写字母 |
| | 匹配 "|"之前或之后的表达式 | ab| cd | ab或者cd |
表2:
符号 | 含义 | 示例 | 说明 | 匹配输入 |
---|---|---|---|---|
. | 匹配除\n外的任意字符 | c..d | 以c开头,d结尾中间包括任意两个字符长度未4的字符串 | cdbd,cshd |
\\d | 匹配单个字符,相当于[0-9] | \\{3} (\\d)? | 包含3个或者4个数字的字符串 | 123,2434 |
\\D | 匹配单个非数字字符,相当于[^0-9] | (\\d)+\\D | 一个或者多个数字开头后面接一个非数字字符 | 1%,32( |
\\w | 匹配单个数字,大小写字母字符相当于[0-9a-zA-Z] | \\d{3}\\w{4} | 以三个数字开头长度为7的数字字母字符串 | 123a3bd,563asd5 |
\\W | 匹配单个非数字,大小写字母相当于[^0-9a-zA-Z] | \\W\\d{2} | 以一个非数字字母开头两个数字结尾的字符串 | $12,^34 |
* | 指定字符重复0次或n次(无要求零到多) | (abc)* | 仅包含任意个abc的字符串,等效于\w* | abc,abcabc |
+ | 指定字符重复1次或n次 (至少1次)1到多 | m+(abc)* | 至少以1个m开头,后面你接任意个abc | mabc,mmabcabc |
? | 指定字符重复0次或1次(最多1次) | m+abc? | 至少以1个m开头 后接ab或者abc | mab,mmabc |
{n} | 只能输入n个字符 | [abcd]{3} | 由abc组成任意3个字符串 | aaa,bbb |
{n.} | 指定至少n个匹配 | [abcd]{3,} | 由abcd组成的任意长度不小于3的字符串 | aaa,aadcd |
{n,m} | 指定至少个但不多于m个匹配 | [abcd]{3,4} | 由abcd组成的任意长度不小于3不大于5的字符串 | acd,abcd,aaaa |
三. 字符匹配符含义说明
- [a-z]
表示可以匹配a-z中任意一个字符,比如使用[a-z]去匹配a123c会得到a,c。 - Java正则表达式默认是区分大小写的,如何使其不区分大小写
- (?!)abc 表示abc都不区分大小写
- a(?!)bc 表示bc都不区分大小写
- a((?!)b)c 表示只有b不区分大小写
- [ ^a-z ]说明:
[ ^a-z ]表示可以匹配不是a-z中的任意一个字符,比如使用[ ^a-z ]去匹配 ab237(c 会得到结果237(,[^A-Z]同理。
- \\d 表示匹配0-9中任意一个数字
- \\D 表示可以匹配不是0-9中的任意一个数字
- \\w 表示匹配任意英文字符,数字和下划线相当于[0-9a-zA-z_]
- \\W 相当于[ ^0-9a-zA-z_ ]
- \\s 匹配任意空白字符(空格,制表符)
- \\S 匹配任意非空白字符,和\\s刚好相反
- 元字符-定位符
符号 | 含义 | 示例 | 说明 | 匹配输入 |
---|---|---|---|---|
^ | 指定起始字符 | ^ [ 0-9 ]+[a-z]* | 以至少一个数字开头,后接任意个小写字母的字符串 | 123,6ac |
$ | 指定结束字符 | 1\\-[a-z]$ | 以1个数字开头后接连字符"-",并以至少一个小写字母结尾的字符串 | 1-a,2-c |
\\b | 匹配字符串的边界 | han\b | 边界的意思是子串间有空格,或者是目标串的结束位置 | hanshupi sphan nnhan |
\B | 匹配目标字符串的非边界 | han\B | hanshupi sphan nnhan |
- 正则表达式分组
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获,捕获匹配的子字符串,编号为零的第一个捕获是由整个正则表达式匹配的文本,其他捕获结果则根据左括号的顺序从1开始编号 |
(?<name> pattern) | 命明捕获,将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含标点符号且不能以数字开头。可以使用单引号代替尖括号,例如(?‘name’) |
(?:pattern) | 匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配,这对于用"or"字符(|)组合模式部件的情况很有用。例如industr(?:y|ies) 是比 ‘industry| industries’ 更经济的表达式 |
(?=pattern) | 它是一个非捕获匹配。例如’Windows (?=95|98|NT|2000)’ 匹配 "Windows 2000"中的 “Windows”,但不匹配 “Windows 3.1"中的"Windows”。 |
(?!pattern) | 该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如’Windows(?!95|98|NT|2000)’ 匹配 “Windows 3.1"中的"windows”, 但不匹配"Windows 2000"中的"Windows"。 |
四. 分组、捕获 、反向引用
- 提出需求
找出一段文本中的四个数字连在一起的子串,满足第1位与第4位相同,第2位与第3位相同,比如1221,5775,… - 分组
可以使用圆括号组成分组,一个圆括号的部分我们可以看作一个子表达式的一个分组。 - 捕获
把正则表达式中子表达式/分组匹配的的内容,保存到内存中以数字编号或显示命明的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0表示整个表达式。 - 反向引用
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个称为反向引用。这种引用既可以是写在正则表达式的内部,也可以是写在正则表达式的外部,反向引用\\分组号,外部引用$分组号。
案例
- 匹配两个连续的相同数字
- 匹配五个连续相同的数字
- 匹配个位与千位相同,十位与百位相同的数,5115,1771
五. 案例
- 案例1
String content = "a11c8";
String repStr = "[a-z]";
Pattern compile = Pattern.compile(repStr);
Matcher matcher = compile.matcher(content);
while (matcher.find()){
System.out.print(matcher.group(0) + " ");
}
运行结果:
2. 案列2
String content = "adjha1123 asd5678";
String RepStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";
Pattern pattern = Pattern.compile(RepStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.print(matcher.group(1) + " ");
System.out.print(matcher.group("g1") + " ");
System.out.print(matcher.group(2) + " ");
System.out.println(matcher.group("g2"));
}
运行结果:
3. 案例3
String content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17";
String repStr = "^((https|http)://)([\\w-]+\\.)+[\\w]+(\\/[\\w?=/-]*)?$";
Pattern pattern = Pattern.compile(repStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()) {
System.out.println("满足匹配");
} else {
System.out.println("不满足匹配");
}
//整体匹配
System.out.println("");
//使用一个整体来匹配
boolean matches = Pattern.matches(repStr, content);
运行结果:
满足匹配
- 案例4
String content= "hello world jack nice look loop overLook hello hello";
String repStr = "hello";
//替换将一个字符全部替换成另一个字符
String newStr = "hello";
Pattern pattern1 = Pattern.compile(newStr);
Matcher matcher1 = pattern1.matcher(content);
System.out.println(matcher1.replaceAll("牛逼"));
运行结果:
5. 案例5
String content = "jac11k john work 1551look 2332hard 6776extraordinary";
String content = "12345-999888000";
String expStr = "(\\d)\\1"; //查找到两个相同的数据
String expStr1 = "(\\d)(\\d)\\2\\1";// 查找第一位与第四位相同的数据
//判断数据引用的时候应该判断其是否有括号 如果有的话应该\\2判断位置
String expStr2 = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}"; //判断content是否满足格式
Pattern pattern = Pattern.compile(expStr2);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
运行结果:
6. 案例6
String content = "我我....要.....学学学学Java!";
String repStr = "";
Pattern compile = Pattern.compile("[.]*");
Matcher matcher = compile.matcher(content);
while (matcher.find()){
System.out.println(matcher.group(0));
}
//首先找到需要去除的数据
content = matcher.replaceAll("");
System.out.println(content);
//将重复的数据去除
Pattern compile1 = Pattern.compile("(.)\\1+");
Matcher matcher1 = compile1.matcher(content);
//使用外部引用计算
// content = matcher1.replaceAll("$1");
// System.out.println(content);
//使用一句话来计算
content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println(content);
//直接匹配
content = "13990909090";
if(content.matches("(138|139)\\d{8}")) {
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
content = "jack$keen?pool#1234+asd";
String[] split = content.split("$|#|\\?|\\+");
for (String s : split) {
System.out.println(s);
}
运行结果:
0-9 ↩︎