正则表达式:
- regular expression => RegExp
1、底层实现 :
1.1 什么是分组:(例如:2023)
1.不分组:\\d\\d\\d\\d
2.分组:(\\d\\d)(\\d\\d),正则表达式中有 () 表示分组,一个括号为一组
1.2 matcher.find():完成的任务
1.根据指定的规则,定位满足规则的子字符串
2.找到后,将子字符串的开始的索引记录到 matcher 对象的属性 int groups;
a.不分组:(例如:2023)
i.把该子字符串开始位置的索引记录到 groups[0] = 0 中 ,结束的索引+1的值记录到 groups[1] = 4中;
b.分组:(例如:(20)(23))
i.把该子字符串开始位置的索引记录到 groups[0] = 0 中 ,结束的索引+1的值记录到 groups[1] = 4中;
ii.记录 1 组 () 匹配的字符串 groups[2] = 0,groups[3] = 2
iii.记录 2 组 () 匹配的字符串 groups[4] = 2,groups[5] = 4
iv.如果有更多的分组以此类推
3.同时记录 oldLast 的值为子字符串结束位置的索引+1,即下次执行 find 方法时,就从 lodLast 开始
a.例如 groups[0] = 0 和 groups[1] = 4 的记录的位置,oldLast = 4,从 content 开始截取子字符串返回,就是 [0, 4) 左闭右开
b.如果再次执行 find 方法,仍然按上面的执行
1.3 小结:
1.如果正则表达式有 () 即分组
2.取出匹配的字符串规则如下:
a.group(0) 表示匹配到的子字符串
b.group(1) 表示匹配到的子字符串的第一组子串
c.group(2) 表示匹配到的子字符串的第二组子串
d.以此类推,但是不能越界
1.4 例子:
String content = "……";
// 1、\\d 表示一个任意的数字
//String regStr = "\\d\\d\\d\\d";
String regStr = "(\\d\\d)(\\d\\d)";
// 2、创建模式对象[即正则表达式对象]
Pattren pattern = Pattern.compile(regStr);
// 3、创建匹配器
// 说明:创建匹配器 matcher,按照正则表达式的规则去匹配 content 字符串
Matcher matcher = Pattern.matcher(content);
// 4、开始匹配
while(matcher.find()){
System.out.println("找到:" + matcher.group(0));
System.out.println("第一组为:" + matcher.group(1));
System.out.println("第二组为:" + matcher.group(2));
}
2、语法:
- 元字符(Metacharacter)- 转义字符 \\
- \\ 符号 说明:
- 在使用正则表达式去检索某些特殊字符时,需要用到转义字符,否则检索不到结果,甚至报错
- 在 Java 中的正则表达式这个,两个 \\ 代表其他语言中的一个 \
- 需要用到转义符号的字符有:. * + ( ) $ / \ ? [ ] ^ { }
例如:
匹配(:\\(
匹配.:\\.
2.1 限定符
- 用于指定其前面的字符和组合项连续出现多少次
符号含义示例说明匹配输入*指定字符重复 0 次或 n 次(无要求) 0 到多(abc)*仅包含任意个 abc 的字符串,等效于 \w*abc、abcabcabc+指定字符重复 1 次或 n 次(至少一次) 1 到多m+(abc)*以至少 1 个 m 开头,后接任意个 abc 的字符串m、mabc、mabcabc?指定字符重复 0 次或 1 次(最多一次) 0 到 1m+abc?以至少 1 个 m 开头,后接 ab 或 abc 的字符串mab、mabc、mmmab、mmabc{n}只能输入 n 个字符[abcd]{3}由 abcd 中字母组成的任意长度为 3 的字符串abc、 dbc、adc{n,}指定至少 n 个匹配[abcd]{3,}由 abcd 中字母组成的任意长度不小于 3 的字符串aab、dbc、aaabdc{n,m}指定至少 n 个但不多于 m 个匹配[abcd]{3,5}由 abcd 中字母组成的任意长度不小于 3,不大于 5 的字符串abc、abcd、aaaaa、bcdab
- 细节:
- 非贪婪匹配和贪婪匹配:
?当此字符紧随任何其他限定符 (*、+、?、{n}、{n,m}) 之后时,匹配模式是 "非贪心的"。"非贪心的" 模式匹配搜索到的、尽可能短的字符串,而默认的 "贪心的" 模式匹配搜索到的、尽可能长的字符串。例如,在字符串 "oooo" 中,"o+?" 只匹配单个 "o",而 "o+" 匹配所有 "o"。
String content = "hello111111 ok";
//String regStr = "\\d+"; //默认是贪婪匹配
String regStr = "\\d+?"; 1/非贪婪匹配
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
2.2 选择匹配符
- 在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个,这时需要使用到选择匹配符号 |
符号含义示例解释|匹配 “|” 之前或之后的表达式ab|cdab 或者 cd
2.3 分组组合和反向引用符:
2.3.1 分组、捕获和反向引用的概念:
1.分组:
a.我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看作是一个子表达式 / 一个分组。
2. 捕获:
a.把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为 1,第二个为 2,以此类推。组 0 代表的是整个正则式
3.反向引用:
a.圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用 \\ 分组号,外部反向引用 $ 分组号
2.3.2 捕获分组:
常用分组构造形式说明(pattern)非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从 1 开始自动编号。(?
// 下面就是非命名分组
// 说明
// 1. matcher.group(0) 得到匹配到的字符串
// 2. matcher.group(1) 得到匹配到的字符串的第 1 个分组内容
// 3. matcher.group(2) 得到匹配到的字符串的第 2 个分组内容
// String regStr = "(\\d\\d)(\\d\\d)";//匹配 4 个数字的字符串
//命名分组: 即可以给分组取名
String regStr = "(?
I
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"));
}
2.3.3 非捕获分组:
常用分组构造形式说明(?:pattern)匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用 "or" 字符(|) 组合模式部件的情况很有用。例如,'industr(?:ylies) 是比 'industry | industries' 更经济的表达式。(?=pattern)它是一个非捕获匹配。例如,'Windows (?=95|98|NT|2000)’ 匹配 "Windows 2000" 中的 "Windows",但不匹配 "Windows 3.1" 中的 "Windows"。(?!pattern)该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如,'Windows (?!9598|NT|2000)' 匹配 "Windows 3.1" 中的 "Windows",但不匹配"Windows 2000"中的 "Windows"。
2.3.4 反向引用:
举例:
1、要匹配两个连续的相同数字:
1.(\\d)\\1
2、要匹配五个连续的相同数字:
1.(\\d)\\1{4}
3、要匹配个位与千位相同,十位与百位相同的数:
1.如 1221、2332:
2.(\\d)(\\d)\\2\\1
4、在字符串中检索商品编号:
1.形式如:12321-333999111,这样的号码:
2.要求满足前面是一个五位数,然后一个 - 号,然后是一个九位数,连续的每三位都要相同
3.\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}
5、结巴去重案例:
1.去掉 . 和重复的字
String content = "我....我要....学学学学学....编程Java";
System.out.println("content=" + content);
// 1、去掉所有的.
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = Pattern.matcher(content);
// 2、去掉重复的字
// 思路:
//(1)使用 (.)\\1+
//(2)使用反向引用 $1 来替换匹配到的内容
// 注意:因为正则表达式变化,所以需要重置 matcher
// pattern = Pattern.compile("(.)\\1+"); // 分组的捕获内容记录到 $1
// matcher = Pattern.matcher(content);
// while(matcher.find()){
// System.out.println("找到=" + matcher.group(0));
// }
// 使用 反向引用 $1 来替换匹配到的内容
// content = matcher.replaceAll("$1");
// System.o