编写代码时经常遇到正则,做个笔记备注!
一. 正则表达式
正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。元字符使正则表达式具有处理能力。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
1.1. 元字符
1.1.1.基本的元字符
元字符 | 说明 |
---|---|
. | 匹配任意单个字符 |
| | 逻辑或操作符 |
[] | 定义一个字符集合,匹配该集合中的一个字符 |
[^] | 对字符集合求非(是对整个集合求非,而不是紧挨着^符号的字符) |
- | 在字符集合中定义一个区间。如[A-Za-z] |
\ | 对下一个字符转义。比如\n表示换行,\\等。 |
1.1.2. 数量元字符
元字符 | 说明 |
---|---|
* | 匹配前一个字符(子表达式)零次或多次 |
*? | *的懒惰型版本(防止正则表达式的“贪婪性”) |
+ | 匹配前一个字符或子表达式一次或多次 |
+? | +的懒惰型版本 |
? | 匹配前一个字符或子表达式零次或一次 |
{n} | 匹配前一个字符或子表达式的n次重复,比如[A-Z]{6}表示匹配由六个大写字母组成的字符串。 |
{m,n} | 匹配至少m次至多n次 |
{m,} | 匹配至少m次 |
{m,}? | {m,}的懒惰型版本 |
1.1.3. 位置元字符
元字符 | 说明 |
---|---|
^ | 行首,注意与[^]区别 |
$ | 行尾 |
\< | 单词开头 |
\> | 单词结尾 |
\b | 单词边界(单词的开头和结束) |
\B | /b的反义 |
1.1.4. 特殊元字符
元字符 | 说明 |
---|---|
[\b] | 匹配一个退格字符 |
\c | 匹配一个控制字符 |
\d | 匹配任意一个数字字符,等价于[0-9] |
\D | /d的反义 |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\s | 匹配一个空白字符 |
\S | /s的反义 |
\t | 制表符 |
\v | 垂直制表符 |
\w | 匹配任意字母、数字、下划线。等价于[A-Za-z0-9_] |
\W | /w的反义 |
\x | 匹配一个十六进制数字 |
\0 | 匹配一个八进制数字 |
1.1.5. 回溯引用和前后查找
元字符 | 说明 |
---|---|
() | 定义一个子表达式 |
/1 | 第一个子表达式,同理/2表示第2个子表达式。/0通常表示整个正则表达式。 |
?= | 向前查找 |
?<= | 向后查找 |
?! | 负向前查找 |
?!= | 负向后查找 |
?() | 条件(if then) |
?()| | 条件(if then else) |
1.2. 贪婪与懒惰
- *?,重复任意次,但尽可能少重复
- +?,重复1次或更多次,但尽可能少重复
- ??,重复0次或1次,但尽可能少重复
- {n,m}?,重复n次到m次,但尽可能少重复
- {n,}?,重复n次以上,但尽可能少重复
1.3. 事例
"^The":表示所有以"The"开始的字符串("There","The cat"等);
"of despair$":表示所以以"of despair"结尾的字符串;
"^abc$":表示开始和结尾都是"abc"的字符串——呵呵,只有"abc"自己了;
"notice":表示任何包含"notice"的字符串。
'*','+'和'?'这三个符号,表示一个或一序列字符重复出现的次数。它们分别表示“没有或更多”,“一次或更多”还有“没有或一次”。下面是几个例子:
"ab*":表示一个字符串有一个a后面跟着零个或若干个b。("a", "ab", "abbb",……);
"ab+":表示一个字符串有一个a后面跟着至少一个b或者更多;
"ab?":表示一个字符串有一个a后面跟着零个或者一个b;
"a?b+$":表示在字符串的末尾有零个或一个a跟着一个或几个b。
也可以使用范围,用大括号括起,用以表示重复次数的范围。
"ab{2}":表示一个字符串有一个a跟着2个b("abb");
"ab{2,}":表示一个字符串有一个a跟着至少2个b;
"ab{3,5}":表示一个字符串有一个a跟着3到5个b。
请注意,你必须指定范围的下限(如:"{0,2}"而不是"{,2}")。还有,你可能注意到了,'*','+'和 '?'相当于"{0,}","{1,}"和"{0,1}"。
还有一个'|',表示“或”操作:
"hi|hello":表示一个字符串里有"hi"或者"hello";
"(b|cd)ef":表示"bef"或"cdef";
"(a|b)*c":表示一串"a""b"混合的字符串后面跟一个"c";
'.'可以替代任何字符:
"a.[0-9]":表示一个字符串有一个"a"后面跟着一个任意字符和一个数字;
"^.{3}$":表示有任意三个字符的字符串(长度为3个字符);
方括号表示某些字符允许在一个字符串中的某一特定位置出现:
"[ab]":表示一个字符串有一个"a"或"b"(相当于"a¦b");
"[a-d]":表示一个字符串包含小写的'a'到'd'中的一个(相当于"a¦b¦c¦d"或者"[abcd]");
"^[a-zA-Z]":表示一个以字母开头的字符串;
"[0-9]%":表示一个百分号前有一位的数字;
",[a-zA-Z0-9]$":表示一个字符串以一个逗号后面跟着一个字母或数字结束。
你也可以在方括号里用'^'表示不希望出现的字符,'^'应在方括号里的第一位。(如:"%[^a-zA-Z]%"表 示两个百分号中不应该出现字母)。
值得注意的是,java里有许多符号需要转义:"\"对应"\\","("对应"\("等。
二. 正则匹配
String str = "Hello,World! in Java."; Pattern pattern = Pattern.compile("W(or)(ld!)"); Matcher matcher = pattern.matcher(str); |
接下来就能用Matcher的方法来查询匹配的结果了。
boolean matches()
boolean lookingAt()
boolean find()
boolean find(int start)
2.1. matches
public static boolean matches(String regex, CharSequence input);
编译给定正则表达式并尝试将给定输入与其匹配。
如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。
- matcher.start() 返回匹配到的子字符串在字符串中的索引位置.
- matcher.end()返回匹配到的子字符串的最后一个字符在字符串中的索引位置.
- matcher.group()返回匹配到的子字符串
调用此便捷方法的形式:
Pattern.matches(regex, input);
Pattern.compile(regex).matcher(input).matches() ;
如果要多次使用一种模式,编译一次后重用此模式比每次都调用此方法效率更高。
参数:
regex - 要编译的表达式
input - 要匹配的字符序列
抛出:
PatternSyntaxException - 如果表达式的语法无效
2.2. lookingAt
boolean lookingAt():
2.3. find
public boolean find();
尝试查找与该模式匹配的输入序列的下一个子序列。
此方法从匹配器区域的开头开始,如果该方法的前一次调用成功了并且从那时开始匹配器没有被重置,则从以前匹配操作没有匹配的第一个字符开始。
如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。
返回:
当且仅当输入序列的子序列匹配此匹配器的模式时才返回 true。
2.4.find(int start)
boolean find(int start):
重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
如果匹配成功,则可通过 start、end 和 group 方法获取更多信息,而 find() 方法的后续调用将从此匹配操作未匹配的第一个字符开始。
返回:
当且仅当从给定索引开始的输入序列的子序列匹配此匹配器的模式时才返回 true。
抛出:
IndexOutOfBoundsException- 如果开始点小于零或大于输入序列的长度。
2.5. group
组是用括号划分的正则表达式,使用组的编号来引用某个组。组号为0表示整个表达式,组号为1表示被第一个括号括起来的组,以此类推。举个例子:
0: ABCD 1:ABC 2:C
Matcher中和Group相关的方法:
public String group():
public String group(i):
public int start(int group):
public int end(int group):
2.6. 事例
private static Set<String> htmlLabel = new HashSet<String>(); private static Set<String> htmlMethod = new HashSet<String>(); static { htmlLabel.add("script"); htmlLabel.add("iframe"); htmlMethod.add("alert"); htmlMethod.add("prompt"); htmlMethod.add("confirm"); } private static String filterHtml(String str) { if (str == null) return null; String start, end; Pattern pattern; String group; for (String label : htmlLabel) { start = "<" + label; end = "</" + label; pattern = Pattern.compile("<\\s*"+label, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(str); while (matcher.find()) { group = matcher.group(); str = str.replaceAll(group, "<" + group.substring(1)); } pattern = Pattern.compile("</\\s*"+label, Pattern.CASE_INSENSITIVE);// Pattern.CASE_INSENSITIVE忽略大小写 matcher = pattern.matcher(str); while (matcher.find()) { group = matcher.group(); str = str.replaceAll(group, "<" + group.substring(1)); } } for (String method : htmlMethod) { start = method + "\\s*\\("; pattern = Pattern.compile(start, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(str); while (matcher.find()) { group = matcher.group(); group = group.substring(0, group.length() - 1) + "\\("; String replce = group.substring(0, group.length() - 1) + "("; str = str.replaceAll(group, replce); } } log.info(str); return str; } |
2.7. 其他
compile()方法还有一个版本,它需要一个控制正则表达式的匹配行为的参数:
Pattern Pattern.compile(String regex, int flag)
flag的取值范围如下:
编译标志 | 效果 |
---|---|
Pattern.CANON_EQ | 当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达式"a/u030A"会匹配"?"。默认情况下,不考虑"规范相等性(canonical equivalence)"。 |
Pattern.CASE_INSENSITIVE | 默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配。要想对Unicode字符进行大小不明感的匹配,只要将UNICODE_CASE与这个标志合起来就行了。 |
Pattern.COMMENTS | 在这种模式下,匹配时会忽略(正则表达式里的)空格字符(注:不是指表达式里的"//s",而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。 |
Pattern.DOTALL | 在这种模式下,表达式'.'可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式'.'不匹配行的结束符。 |
Pattern.MULTILINE | 在这种模式下,'^'和'$'分别匹配一行的开始和结束。此外,'^'仍然匹配字符串的开始,'$'也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。 |
Pattern.UNICODE_CASE | 在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不明感的匹配。默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。 |
Pattern.UNIX_LINES | 在这个模式下,只有'/n'才被认作一行的中止,并且与'.','^',以及'$'进行匹配。 |
参考:正则匹配, matcher.find()和 matcher.matches()的区别, JAVA 正则表达式 (超详细)