目录
一.正则表达式
二.正则表达式的底层实现
1.matcher.find()
2.matcher.groups()
三.正则表达式的语法
1.正则转义符
2.正则表达式支持字符
3.捕获组(capture group)
(1)普通捕获组
(2)命名捕获组
(3)补充:非捕获组
四.正则表达式的常见应用
1.验证字符串是否全是汉字
2.验证字符串是否是邮编
3.验证字符串是否是QQ号码
4.验证字符串是否是手机号码
5.验证字符串是否是url
五.正则表达式常用三个类
1.Pattern类
matches()方法
2.Matcher类
(1)索引方法
(2)查找方法
(3)替换方法
3.PatternSyntaxException类
(1)public String getDescription()
(2)public int getIndex()
(3)public String getPattern()
(4)public String getMessage()
六.反向引用
1.分组
2.捕获
3.反向引用
(本文笔记整理思路来自韩顺平老师的Java正则表达式专题)
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是一个很牛逼的字符串处理工具。
正则表达式中有()表示分组,第1个()表示第1组,第2个()表示第2组...
(1)根据指定的规则,定位满足规则的子字符串
(2)找到后,将子字符串的开始的索引记录到matcher对象的属性 int[] groups,把该子字符串的结束的索引的值+1记录到groups[1]中
a.groups[0]为子字符串开始的索引,groups[1]为该子字符串的结束的索引的值+1
b.groups[2]为匹配到的该字符串第1组的开始索引,groups[3]为匹配到的该字符串第1组的结束索引+1
c.groups[4]为匹配到的该字符串第2组的开始索引,groups[5]为匹配到的该字符串第2组的结束索引+1
依此类推……
(3)同时记录oldLast的值为该子字符串的结束的索引的值+1,即groups[1],即下次执行find时,就从groups[1]开始匹配
(代码中参数含义不明白的,参考Java中的Matcher类)
(1)matcher.groups(0)根据groups[0]和groups[1]记录的位置,从content开始截取字符串返回索引为[groups[0],groups[1])的位置,即返回匹配到的子字符串
(2)matcher.groups(1)返回匹配到的子字符串第1组的字符串,matcher.groups(2)返回匹配到的子字符串第2组的字符串,依此类推……
(3)如果你输入的group大于了分组数,会抛出IndexOutOfBoundsException异常
元符号-转义号\\
\\符号说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义字符,否则检测不到结果,甚至会报错。
需要用到转义符的字符有:. * + ( )$ / \ ? [ ] ^ { }
注意:在Java的正则表达式中,两个\\代表其他语言中的一个\
字符 | 解释 |
---|---|
X | 字符x(x 可代表任何合法的字符) |
\0mnn | 八进制数 0mnn 所表示的字符 |
\xhh | 十六进制值 0xhh 所表示的字符 |
\uhhhh | 十六进制值 0xhhhh 所表示的 Unicode 字符 |
\t | 制表符(“\u0009”) |
\n | 新行(换行)符(‘\u000A’) |
\r | 回车符(‘\u000D’) |
\f | 换页符(‘\u000C’) |
\a | 报警(bell)符(‘\u0007’) |
\e | Escape 符(‘\u001B’) |
\cx | x 对应的的控制符。例如,\cM 匹配 Ctrl-M。x 值必须为 A~Z 或 a~z 之一。 |
特殊字符 | 说明 |
---|---|
$ | 匹配一行的结尾。要匹配 $ 字符本身,请使用\$ |
^ | 匹配一行的开头。要匹配 ^ 字符本身,请使用\^ |
() | 标记子表达式的开始和结束位置。要匹配这些字符,请使用\( 和\) |
[] | 用于确定中括号表达式的开始和结束位置。要匹配这些字符,请使用\[ 和\] |
{} | 用于标记前面子表达式的出现频度。要匹配这些字符,请使用\{ 和\} |
* | 指定前面子表达式可以出现零次或多次。要匹配 * 字符本身,请使用\* |
+ | 指定前面子表达式可以出现一次或多次。要匹配 + 字符本身,请使用\+ |
? | 指定前面子表达式可以出现零次或一次。要匹配 ?字符本身,请使用\? |
. | 匹配除换行符\n 之外的任何单字符。要匹配. 字符本身,请使用\. |
\ | 用于转义下一个字符,或指定八进制、十六进制字符。如果需匹配\ 字符,请用\\ |
| | 指定两项之间任选一项。如果要匹配丨 字符本身,请使用\| |
预定义字符 | 说明 |
---|---|
. | 可以匹配任何字符 |
\d | 匹配 0~9 的所有数字 |
\D | 匹配非数字 |
\s | 匹配所有的空白字符,包括空格、制表符、回车符、换页符、换行符等 |
\S | 匹配所有的非空白字符 |
\w | 匹配所有的单词字符,包括 0~9 所有数字、26 个英文字母和下画线_ |
\W | 匹配所有的非单词字符 |
方括号表达式
方括号表达式 说明 表示枚举 例如[abc]
表示 a、b、c 其中任意一个字符; [gz]
表示 g、z 其中任意一个字符 表示范围:- 例如 [a-f]
表示 a~f 范围内的任意字符; [\\u0041-\\u0056]
表示十六进制字符 \u0041 到 \u0056 范围的字符。范围可以和枚举结合使用,如 [a-cx-z]
,表示 a~c、x~z 范围内的任意字符 表示求否:^ 例如 [^abc]
表示非 a、b、c 的任意字符; [^a-f]
表示不是 a~f 范围内的任意字符 表示“与”运算:&& 例如 [a-z&&[def]]
是 a~z 和 [def] 的交集,表示 d、e
f[a-z&&^bc]]
是 a~z 范围内的所有字符,除 b 和 c 之外
[ad-z] [a-z&&[m-p]]
是 a~z 范围内的所有字符,除 m~p 范围之外的字符 表示“并”运算 并运算与前面的枚举类似。例如 [a-d[m-p]]
表示 [a-dm-p]
边界匹配符
边界匹配符 说明 ^ 行的开头 $ 行的结尾 \b 单词的边界 \B 非单词的边界 \A 输入的开头 \G 前一个匹配的结尾 \Z 输入的结尾,仅用于最后的结束符 \z 输入的结尾贪婪模式 | 勉强模式 | 占用模式 | 说明 |
---|---|---|---|
X? | X?? | X?+ | X表达式出现零次或一次 |
X* | X*? | X*+ | X表达式出现零次或多次 |
X+ | X+? | X++ | X表达式出现一次或多次 |
X{n} | X{n}? | X{n}+ | X表达式出现 n 次 |
X{n,} | X{n,}? | X{n,}+ | X表达式最少出现 n 次 |
X{n,m} | X{n,m}? | X{n,m}+ | X表达式最少出现 n 次,最多出现 m 次 |
(如上几表来源于Java正则表达式详解)
补充:
Java正则表达式默认是区分字母大小写的,如要实现不区分大小写
(?i)abc表示abc都不区分大小写
a(?i)bc表示bc不区分大小写
a((?i)b)c表示只有b不区分大小写
Pattern pattern=Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
//当创建Pattern对象时,指定Pattern.CASE_INSENSITIVE,表示匹配不区分字母大小写
细节:Java匹配默认贪婪匹配,即尽可能匹配多的,比如"a{3,4}",表示匹配aaa或者aaaa,但是优先匹配aaaa,例如原字符串中包括"aaaaaa",匹配"a{3,4}"时,会找到"aaaa"
捕获组分为:普通捕获组和命名捕获组
从正则表达式左侧开始,每出现一个左括号"("记做一个分组,分组编号从 1 开始。0 代表整个表达式。常用分组构造形式(pattern)
对于时间字符串:2017-04-25,表达式如下
(\\d{4})-((\\d{2})-(\\d{2}))
有 4 个左括号,所以有 4 个分组:
编号 | 捕获组 | 匹配 |
---|---|---|
0 | (\d{4})-((\d{2})-(\d{2})) | 2017-04-25 |
1 | (\d{4}) | 2017 |
2 | ((\d{2})-(\d{2})) | 04-25 |
3 | (\d{2}) | 04 |
4 | (\d{2}) | 25 |
每个以左括号开始的捕获组,都紧跟着 ?,而后才是正则表达式。
常用分组构造形式(?
注意:用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?'name')
对于时间字符串:2017-04-25,表达式如下:
(?\\d{4})-(? (? \\d{2})-(? \\d{2}))
有 4 个命名的捕获组,分别是:
编号 | 名称 | 捕获组 | 匹配 |
---|---|---|---|
0 | 0 | (?\d{4})-(?(?\d{2})-(?\d{2})) | 2017-04-25 |
1 | year | (?\d{4})- | 2017 |
2 | md | (?(?\d{2})-(?\d{2})) | 04-25 |
3 | month | (?\d{2}) | 04 |
4 | date | (?\d{2}) | 25 |
命名的捕获组同样也可以使用编号获取相应值。
在左括号后紧跟 ?:,而后再加上正则表达式,构成非捕获组 (?:Expression)。
常用构造形式(?:pattern)
对于时间字符串:2017-04-25,表达式如下:
(?:\\d{4})-((\\d{2})-(\\d{2}))
这个正则表达式虽然有四个左括号,理论上有 4 个捕获组。但是第一组 (?:\d{4}),其实是被忽略的。当使用 matcher.group(4) 时,系统会报错。
编号 | 捕获组 | 匹配 |
---|---|---|
0 | (\d{4})-((\d{2})-(\d{2})) | 2017-04-25 |
1 | ((\d{2})-(\d{2})) | 04-25 |
2 | (\d{2}) | 04 |
3 | (\d{2}) | 25 |
(捕获组参考:Java正则表达式的捕获组(capture group))
(?=pattern)和(?!pattern)也是非捕获匹配,由于应用不多,就不介绍了,感兴趣的可以私下去了解
设置Pattern pattern=Pattern.compile("^[\u0391-\uffe5]+$");
设置Pattern pattern=Pattern.compile("^\\d{6}$");
设置Pattern pattern=Pattern.compile("^[1-9]d{4,9}$");
(要求:必须以13,14,15,18开头的11位数)
设置Pattern pattern=Pattern.compile("^1[3|4|5|8]\\d{9}$");
设置Pattern pattern=Pattern.compile("^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$");
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
举个例子:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:
序号 | 方法及说明 |
---|---|
1 | public int start() 返回以前匹配的初始索引。 |
2 | public int start(int group) 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引 |
3 | public int end() 返回最后匹配字符之后的偏移量。 |
4 | public int end(int group) 返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
举个例子:
注:substring()方法是前闭后开区间
查找方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:
序号 | 方法及说明 |
---|---|
1 | public boolean lookingAt() 尝试将从区域开头开始的输入序列与该模式匹配。 |
2 | public boolean find() 尝试查找与该模式匹配的输入序列的下一个子序列。 |
3 | public boolean find(int start) 重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。 |
4 | public boolean matches() 尝试将整个区域与模式匹配。 |
举个例子:
从上面例子显然能看到,当正则表达式是a*b时可以匹配,当正则表达式是ab时无法匹配,从而能证明matches()是完全匹配。
替换方法是替换输入字符串里文本的方法:
序号 | 方法及说明 |
---|---|
1 | public Matcher appendReplacement(StringBuffer sb, String replacement) 实现非终端添加和替换步骤。 |
2 | public StringBuffer appendTail(StringBuffer sb) 实现终端添加和替换步骤。 |
3 | public String replaceAll(String replacement) 替换模式与给定替换字符串相匹配的输入序列的每个子序列。 |
4 | public String replaceFirst(String replacement) 替换模式与给定替换字符串匹配的输入序列的第一个子序列。 |
5 | public static String quoteReplacement(String s) 返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。 |
由quoteReplacement()的源码以及例子可以看出,quoteReplacement()用于将s转义,通过此方法生成的字符串s将在替换操作中被视为普通的字符串,即该方法转义了斜线'\'和美元符号'$'的特殊含义,使它们不再生效。
PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。
PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。
获取错误的描述。
获取错误的索引。
获取错误的正则表达式模式。
返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示。
举个例子:
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看作是一个子表达式/一个分组。
把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式。
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用\\分组号,外部反向引用$分组号。
例如:
(1)要匹配两个连续的相同数字:(\\d)\\1
(2)要匹配五个连续的相同数字:(\\d)\\1{4}
(3)要匹配个位与千位相同,十位与百位相同的数,例如1221,6556:(\\d)(\\d)\\2\\1
(4)结巴去重案例,例如要将"我我要学学学学编程!"简化成"我要学编程":
content=Pattern.compile("(.)\\1+").matcher(content).replace("$1");