正则表达式
正则表达式是对字符串执行模式匹配的技术
底层实现分析
局部匹配
public class RegexpDemo01 {
public static void main(String[] args) {
String str = "在1958 年,一位名叫Stephen Kleene的数学科学家博尔特," +
"1963,他在Warren McCulloch和Walter Pitts早期工作的基础之上,发表了一篇题目是" +
"《神经网事件的表示法》的论文,1969利用称之为正则集合的数学符号来描述此模型,引入" +
"了正则表达式的概念。1971正则表达式被作为用来描述其称之为“正则集的代数”的一种表达式," +
"因而采用了“正则表达式”这个术语。";
// 匹配正则表达式字符 匹配四个数字
String regexp = "\\d\\d\\d\\d";
// 生成模式对象
Pattern pattern = Pattern.compile(regexp);
// 匹配字符
Matcher matcher = pattern.matcher(str);
/*
* matcher.find()
* 1. 根据 pattern 的规则, 定位满足规则的字符串(比如:1958) 在str的开始索引位置(1)和结束索引位置+1(5)
* 2. 将索引位置记录到数组groups; 的下标 groups[0] = 0 groups[1] = 4
* 3. 并且下一次索引开始等于当前的结束索引位置+1 (设置为 5, 表示下一次匹配从5开始)
* 4. 下次找到数字1963 始索引位置(36)和结束索引位置+1(40)
* 5. 将索引位置记录到数组groups;的下标groups[0] = 0 groups[1] = 4 记住会覆盖之前的数组下标0,1的数组
*
* 特别说明: 当在正则表达式中有()(如(\\d\\d)(\\d\\d) ),则表示分组
* 第1个() 表示第一组 第2个() 表示第2组
* 2.1 groups[0] groups[1] 记录匹配到的完整结果 如: 1958
* 2.2 groups[2] groups[3] 记录匹配到的结果的第一个()的内容的开始索引和结束索引+1 即0和2
* 2.3 groups[4] groups[5] 记录匹配到的结果的第2个()的内容的开始索引和结束索引+1 即2和4
*
* ----------------------------------------------------------------------------------------
*
* matcher.group(index)
* 源码分析
* 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();
* }
* 1. 如果matcher.group(1)从字符串截取下标 groups[0] 到 groups[1]-1 的子字符串
* 2. 如果matcher.group(1) 截取 groups[2] 到 groups[3] 对应索引的子字符串
* 2. 如果matcher.group(2) 截取 groups[4] 到 groups[5] 对应索引的子字符串
*
* 然后循环的执行find和group的方法
*/
while(matcher.find()){ // 如果查找数组找到的值为-1即没有存入数据则返回false
System.out.print(matcher.group(0) + " "); // 1958 1963 1969 1971
}
}
}
整体匹配
public static void main(String[] args) {
String str = "12345";
// 匹配正则表达式字符 匹配四个数字
String regexp = "\\d\\d\\d\\d";
// 匹配字符 完成匹配
boolean isMatch = Pattern.matches(pattern,content);
System.out.println(isMarch); // false
}
正则表达式语法
元字符
转义符
// 转义符:\\
public static void main(String[] args) {
String str = "在1958 年,一位名叫Stephen Kle(ene的(数学科学家博尔特(";
String regexp = "\\("; // ( 会报错
Pattern pattern = Pattern.compile(regexp);
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
System.out.println(matcher.group(0)); // 3个 (
}
}
限定符
用于指定其前面的字符和组合项连续出现多少次
符号 | 含义 | 示例 | 说明 | 匹配输入 |
---|---|---|---|---|
* | 指定字符重复0次或n次(无要求)零到多 | (abc)* | 仅包含任意个abc的字符串,等效于\w* | abc,abcabcabc |
+ | 指定字符重复1次或n次(至少一次) | m+(abc)* | 以至少1个m开头,后接任意个abc的字符串 | m,mabc,mabcabc |
? | 指定字符重复0次或1次(最多一次) | m+abc? | 以至少1个m开头,后接ab或abc的字符串 | mab,mabc,mmmab |
{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,aaaaaa,bcdab |
? | 匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串 | 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时, |
注意:java匹配默认贪婪匹配,即尽可能匹配多的数量 如 a{3,4} : aaaa优先级 > aaa优先级
选择匹配符
在选择匹配某个字符串的时候是选择性的( | )
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
| | 匹配“|”之前或之后的表达式 | ab|bc | ab或者bc |
分组组合
常用分组
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号 |
(?< name >pattern) | 命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如(?‘name’) |
public static void main(String[] args) {
String str = "在1958 年,一位名叫Stephen Kleene的数学科学家博尔特," +
"1963,他在Warren McCulloch和Walter Pitts早期工作的基础之上,发表了一篇题目是" +
"《神经网事件的表示法》的论文,1969利用称之为正则集合的数学符号来描述此模型,引入" +
"了正则表达式的概念。1971正则表达式被作为用来描述其称之为“正则集的代数”的一种表达式," +
"因而采用了“正则表达式”这个术语。";
// 匹配正则表达式字符 匹配四个数字
// 非命名分组
String regexp = "(\\d\\d)(\\d\\d)";
// 生成模式对象
Pattern pattern = Pattern.compile(regexp);
// 匹配字符
Matcher matcher = pattern.matcher(str);
while(matcher.find()){
// 1958 19 58
// 1963 19 63
// 1969 19 69
// 1971 19 71
System.out.print(matcher.group(0) + " ");
System.out.print(matcher.group(1) + " ");
System.out.print(matcher.group(2) + " ");
System.out.println();
}
System.out.println("=====================");
// 命名分组
regexp = "(?<group1>\\d\\d)(?<group2>\\d\\d)";
pattern = Pattern.compile(regexp);
// 匹配字符
matcher = pattern.matcher(str);
while(matcher.find()){
// 1958 19 58
// 1963 19 63
// 1969 19 69
// 1971 19 71
System.out.print(matcher.group(0) + " ");
System.out.print(matcher.group("group1") + " ");
System.out.print(matcher.group("group2") + " ");
System.out.println();
}
}
特别分组
常用分组构造形式 | 说明 |
---|---|
(?:pattern) | 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"o"字符()组合模式部件的情况很有用。例如,'industr(?yies)是比’industryindustries’更经济的表达式 |
(?=pattern) | 它是一个非捕获匹配。例如,Windows(?=9598NT2000)'匹配"Windows2000"中的"Vindows",但不匹配"Vindows3.1"中的"Windows" |
(?!pattern) | 该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如,“Nindows(?!9598NT2000)'匹配"Windows3.1"中的"Windows”,但不匹配"Windows2000"中的"Vindows" |
// (?:pattern)
String str = "罗念笙同学和罗念笙父亲以及罗念笙母亲要一起出席会场";
String regexp = "罗念笙(?:同学|父亲|母亲)"; // 等价于 "罗念笙同学 | 罗念笙父亲 | 罗念笙母亲)"
Pattern pattern = Pattern.compile(regexp);
Matcher matcher = pattern.matcher(str);
while(matcher.fins()){
// 不能使用group(1)捕获分组,但是整体是可以用group(0)捕获
}
// (?=pattern) 和 (?!pattern) 取反关系
字符匹配符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
[ ] | 可接受的字符列表 | [efgh] | e,f,g,h中的任意一个字符 |
[^] | 不接收的字符列表 | [^abc] | a,b,c之外的任意一个字符,包括数字和特殊字符 |
- | 连字符 | A-Z | 任意一个大写字母 |
. | 匹配除\n以外的任何字符 | a…b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 |
\ \d | 匹配单个数字字符,相当于[0-9] | \ \d{3}(\ \d)? | 包含3个或4个数字的字符串 |
\ \D | 匹配单个非数字字符,相当于[ ^ 0-9]] | \ \D(\ \d)* | 以单个非数字字符开头,后接任意个数字字符串 |
\ \w | 匹配单个数字、大小写字母字符相当于[0-9a-zA-Z] | \ \d{3}\ \w{4} | 以3个数字字符开头的长度为7的数字字母字符串 |
\ \W | 匹配单个非数字、大小写字母字符相当于[ ^ 0-9a-zA-Z] | \ \W+\ \d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串 |
\ \ s | 匹配任何空白字符(空格,制表符等) | 空格,制表符 | 无 |
\ \ S | 匹配任何非空白字符 | 除空格,制表符之外 | 无 |
// 细节说明
// 不区分大小写
(?!)abc // abc都不区分大小写
a(?!)bc // abc都不区分大小写
a((?!)b)c // 只有b不区分大小写
Pattern pattern = Pattern.compile(regexp,Pattern.CASE_INSENSITIVE);
定位符
规定要匹配的字符出现的位置
符号 | 含义 | 示例 | 说明 | 匹配输入 |
---|---|---|---|---|
^ | 指定起始字符 | ^ [0-9] + [a- z]* | 以至少1个数字开头,后 串 | 123, 6aa |
$ | 指定结束字符 | ^ [0-9]\ \ - [a-z] + $ | 以1个数字开头后接连字符“-”,并以至少1个小写字母结尾的字符串 | 1-a |
\ \ b | 匹配目标字符串的边界 | lns\ \ b | 这里说的字符串的边界指的是子串间有空格,或者是目标字符串的结束位置 | splns,hallns |
\ \ B | 匹配目标字符串的非边界 | lns \ \ B | 和b的含义刚刚相反 | lnssp,lnsfh |