共计 6471 个字符,预计需要花费 17 分钟才能阅读完成。
作者:小傅哥
博客:https://bugstack.cn
积淀、分享、成长,让本人和别人都能有所播种!😄
一、前言
编程总在实践中出后果!
正则表达式,又称规定表达式。(英语:Regular Expression,在代码中常简写为 regex、regexp 或 RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些合乎某个模式 (规定) 的文本。
正则引擎次要能够分为两大类:一种是 DFA,一种是 NFA。这两种引擎都有了很久的历史(至今二十多年),当中也由这两种引擎产生了很多变体!于是 POSIX 的出台躲避了不必要变体的持续产生。这样一来,支流的正则引擎又分为 3 类:一、DFA,二、传统型 NFA,三、POSIX NFA。
正则也是一种十分有意思的技术,但往往不晓得这些符号的编程在理论应用中该如何应用,因而总结了本篇文章,不便所有小伙伴能够当成一个工具文章应用,不便解决一些须要应用正则的技术内容。
二、规定
1. 罕用符号
- x 字符 x
- \ 反斜线字符
- \0n 带有八进制值 0 的字符 n (0 <= n <= 7)
- \0nn 带有八进制值 0 的字符 nn (0 <= n <= 7)
- \0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7)
- \xhh 带有十六进制值 0x 的字符 hh
- \uhhhh 带有十六进制值 0x 的字符 hhhh
- \t 制表符 (‘\u0009’)
- \n 新行(换行)符 (‘\u000A’)
- \r 回车符 (‘\u000D’)
- \f 换页符 (‘\u000C’)
- \a 报警 (bell) 符 (‘\u0007’)
- \e 本义符 (‘\u001B’)
2. 字母字符
- [abc] a、b 或 c(简略类)
- 1 任何字符,除了 a、b 或 c(否定)
- [a-zA-Z] a 到 z 或 A 到 Z,中间的字母包含在内(范畴)
- [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
- [a-z&&[def]] d、e 或 f(交加)
- [a-z&&2] a 到 z,除了 b 和 c:[ad-z](减去)
- [a-z&&3] a 到 z,而非 m 到 p:[a-lq-z](减去)
3. 预约义字符
- . 任何字符(与行结束符可能匹配也可能不匹配)
- \d 数字:[0-9]
- \D 非数字:4
- \s 空白字符:[\t\n\x0B\f\r]
- \S 非空白字符:5
- \w 单词字符:[a-zA-Z_0-9]
- \W 非单词字符:6
4. POSIX 字符
- \p{Lower} 小写字母字符:[a-z]
- \p{Upper} 大写字母字符:[A-Z]
- \p{ASCII} 所有 ASCII:[\x00-\x7F]
- \p{Alpha} 字母字符:[\p{Lower}\p{Upper}]
- \p{Digit} 十进制数字:[0-9]
- \p{Alnum} 字母数字字符:[\p{Alpha}\p{Digit}]
- \p{Punct} 标点符号:!”#$%&'()*+,-./:;<=>?@[]^_`{|}~
- \p{Graph} 可见字符:[\p{Alnum}\p{Punct}]
- \p{Print} 可打印字符:[\p{Graph}\x20]
- \p{Blank} 空格或制表符:[\t]
- \p{Cntrl} 控制字符:[\x00-\x1F\x7F]
- \p{XDigit} 十六进制数字:[0-9a-fA-F]
- \p{Space} 空白字符:[\t\n\x0B\f\r]
5. Character 类
- \p{javaLowerCase} 等效于 java.lang.Character.isLowerCase()
- \p{javaUpperCase} 等效于 java.lang.Character.isUpperCase()
- \p{javaWhitespace} 等效于 java.lang.Character.isWhitespace()
- \p{javaMirrored} 等效于 java.lang.Character.isMirrored()
6. Unicode 块和类别的类
- \p{InGreek} Greek 块(简略块)中的字符
- \p{Lu} 大写字母(简略类别)
- \p{Sc} 货币符号
- \P{InGreek} 所有字符,Greek 块中的除外(否定)
- [\p{L}&&7] 所有字母,大写字母除外(减去)
7. 边界匹配器
- ^ 行的结尾
- $ 行的结尾
- \b 单词边界
- \B 非单词边界
- \A 输出的结尾
- \G 上一个匹配的结尾
- \Z 输出的结尾,仅用于最初的结束符(如果有的话)
- \z 输出的结尾
8. Greedy 数量词
- X? X,一次或一次也没有
- X* X,零次或屡次
- X+ X,一次或屡次
- X{n} X,恰好 n 次
- X{n,} X,至多 n 次
- X{n,m} X,至多 n 次,然而不超过 m 次
9. Reluctant 数量词
- X?? X,一次或一次也没有
- X*? X,零次或屡次
- X+? X,一次或屡次
- X{n}? X,恰好 n 次
- X{n,}? X,至多 n 次
- X{n,m}? X,至多 n 次,然而不超过 m 次
10. Possessive 数量词
- X?+ X,一次或一次也没有
- X*+ X,零次或屡次
- X++ X,一次或屡次
- X{n}+ X,恰好 n 次
- X{n,}+ X,至多 n 次
- X{n,m}+ X,至多 n 次,然而不超过 m 次
11. Logical 运算符
- XY X 后跟 Y
- X|Y X 或 Y
- (X) X,作为捕捉组
12. Back 援用
- \n 任何匹配的 nth 捕捉组
13. 援用
- \ Nothing,然而援用以下字符
- \Q Nothing,然而援用所有字符,直到 \E
- \E Nothing,然而完结从 \Q 开始的援用
14. 非凡结构(非捕捉)
- (?:X) X,作为非捕捉组
- (?idmsux-idmsux) Nothing,然而将匹配标记 i d m s u x on – off
- (?idmsux-idmsux:X) X,作为带有给定标记 i d m s u x on – off 的非捕捉组 (?=X) X,通过零宽度的正 lookahead
- (?!X) X,通过零宽度的负 lookahead
- (?<=X) X,通过零宽度的正 lookbehind
- (?<!X) X,通过零宽度的负 lookbehind
- (?>X) X,作为独立的非捕捉组
三、案例
1. 字符匹配
"a".matches(".")
- 后果:true
- 形容:. 匹配任意字符
"a".matches("[abc]")
- 后果:true
- 形容:蕴含 abc 任意一个字符都匹配,默认匹配一次
"a".matches("[^abc]")
- 后果:false
- 形容:任何字符,除了 a、b 或 c(否定)
"A".matches("[a-zA-Z]")
- 后果:true
- 形容:a 到 z 或 A 到 Z,中间的字母包含在内(范畴)
"A".matches("[a-z]|[A-Z]")
- 后果:true
- 形容:a 到 z 或 A 到 Z,中间的字母包含在内(范畴)
"A".matches("[a-z(A-Z)]")
- 后果:true
- 形容:a-z,A-Z,匹配范畴雷同,括号是捕捉组
"R".matches("[A-Z&&(RFG)]")
- 后果:true
- 形容:匹配 A-Z 与 RFG 交加
"a_8".matches("\\w{3}")
- 后果:true
- 形容:\w 单词字符等同于 [a-zA-Z_0-9],{3} 匹配三次
"\\".matches("\\\\")
- 后果:true
- 形容:\ 示意的是一个 \
"hello sir".matches("h.*")
- 后果:true
- 形容:. 任何字符,* 匹配零次到屡次
"hello sir".matches(".*ir$")
- 后果:true
- 形容:.* 匹配任意字符 ir$ 确定匹配行的结尾
"hello sir".matches("^h[a-z]{1,3}o\\b.*")
- 后果:true
- 形容:^h 匹配结尾,[a-z]{1,3}o 匹配 1 到 3 次的 a - z 之后匹配字母 o,\b 并不匹配这些单词分隔字符中的任何一个,它只匹配一个地位。匹配的是 o 前面的地位。
"hellosir".matches("^h[a-z]{1,3}o\\b.*")
- 后果:false
- 形容:o 前面跟着 s,是字母,不是空格,\b 不能匹配单词的 o 的边界。
"\n".matches("^[\\s&&[^\\n]]*\\n$")
- 后果:true
- 形容:匹配结尾是一个空格
^[\\s&&[^\\n]]
,且不能是换行符,最初必须是换行\\n$
System.out.println("java".matches("(?i)JAVA"));
- 后果:true
- 形容:(?i)非捕捉组外面这个示意疏忽大小写
2. 模式匹配
2.1 验证匹配
Pattern p = Pattern.compile("[a-z]{3,}");
Matcher m = p.matcher("fgha");
System.out.println(m.matches()); // true,匹配字符 3 次及以上
- 后果:true
- 形容:Pattern 与 Matcher 一起单干 .Matcher 类提供了对正则表达式的分组反对,以及对正则表达式的屡次匹配反对.。独自用 Pattern 只能应用 Pattern.matches(String regex,CharSequence input) 一种最根底最简略的匹配。
2.2 匹配性能
Pattern p = Pattern.compile("\\d{3,5}");
Matcher m = p.matcher("123-4536-89789-000");
System.out.println(m.matches());
m.reset();// 把吃进去的字符吐出来从新匹配,否通过 m2.matches 会吃进去字符 上面的匹配就不胜利
System.out.println(m.find());
System.out.println(m.start() + "-" + m.end()); // 找到了 就把首位地位打印下(必须找到能力打印)System.out.println(m.find());
System.out.println(m.start() + "-" + m.end()); // 找到了 就把首位地位打印下(必须找到能力打印)System.out.println(m.find());
System.out.println(m.start() + "-" + m.end()); // 找到了 就把首位地位打印下(必须找到能力打印)System.out.println(m.find());
System.out.println(m.lookingAt()); // 每次都是才头上开始找
测试后果
false
true
0-3
true
4-8
true
9-14
true
true
- m.matches(),是全量匹配
- m.reset(),把吃进去的字符吐出来从新匹配,否通过 m2.matches 会吃进去字符 上面的匹配就不胜利
- m.find(),查找匹配
- m.start(),匹配到的字符串,开始地位
- m.end(),匹配到的字符串,完结地位
2.3 匹配一般替换
Pattern p = Pattern.compile("java",Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher("java_Java_jAva_jAVa_IloveJava");
System.out.println(m.replaceAll("JAVA"));
- 后果:JAVA_JAVA_JAVA_JAVA_IloveJAVA
- 形容:把所有匹配到的小写字母 java、JavA,都匹配为大写
2.4 匹配逻辑替换
Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher("java_Java_jAva_jAVa_IloveJava fdasfas");
StringBuffer sb = new StringBuffer();
int i = 0;
while (m.find()) {
i++;
if (i % 2 == 0) {m.appendReplacement(sb, "java");
} else {m.appendReplacement(sb, "JAVA");
}
}
m.appendTail(sb);
System.out.println(sb);
- 后果:JAVA_java_JAVA_java_IloveJAVA fdasfas
- 形容:依照程序逻辑
i % 2
,进行单单数替换匹配
2.4 分组匹配
Pattern p = Pattern.compile("(\\d{3,5})([a-z]{2})");
Matcher m = p.matcher("123bb_78987dd_090po");
while(m.find()){System.out.println(m.group(1));
}
-
后果:
123 78987 090 Process finished with exit code 0
- 形容:分组加括号只取数字一组,grop 括号外面第 0 组是整体,第一组是左起第一个括号,第二组是左起第二个括号
2.5 贪心的匹配与不贪心匹配
Pattern p = Pattern.compile("(.{3,10}?)[0-9]");
Matcher m = p.matcher("aaaa5dddd8");
while (m.find()) {System.out.println(m.start() + "-" + m.end());
}
-
后果:
0-5 5-10 Process finished with exit code 0
- 形容:.{3,10}前面没问号就是贪心匹配会陪到最长,如果{3,10}? 加? 号就是懒蛋匹配之匹配起码的,从 3 个开始找。如果这里用 if(m.find)(){m.start()+”-“+m.end()} 那么之匹配第一个
2.6 一般捕捉
Pattern p = Pattern.compile(".{3}");
Matcher m = p.matcher("ab4dd5");
while(m.find()){System.out.println(m.group());
}
-
后果:
ab4 5-10 Process finished with exit code 0
- 形容:每次匹配三个任意字符,用 m.group() 输入。
2.7 非捕捉组(?=a)
Pattern p = Pattern.compile(".{3}(?=a)");
Matcher m = p.matcher("ab4add5");
while (m.find()) {System.out.println("前面不能是 a 的:" + m.group());
}
- 后果:
前面不能是 a 的:ab4
- 形容:(?=a)这个是非捕捉组的意思,最初一个是 a 而且还不把这个 a 取出来!!(?=a)这个要是写在后面就不一样了
Pattern p = Pattern.compile("(?!a).{3}");
Matcher m = p.matcher("abbsab89");
while (m.find()) {System.out.println("后面不能是 a 的:" + m.group());
}
- 后果:后面不能是 a 的:bbs、后面不能是 a 的:b89
- 形容:(?!a)后面不能是 a 的,所以找到整个字符串中 bbs、b89
2.8 去除 >< 号匹配
Pattern p = Pattern.compile("(?!>).+(?=<)");
Matcher m = p.matcher("> 小傅哥 <");
while (m.find()) {System.out.println(m.group());
}
- 后果:小傅哥
- 形容:个别能够匹配网页中的非凡字符串外面的内容信息。
2.9 向前援用
Pattern p = Pattern.compile("(\\d\\d)\\1");
Matcher m = p.matcher("1212");
System.out.println(m.matches());
- 后果:true
- 形容:这外面的 1 是向前援用,12 是第一匹配到的,下一次在匹配进去 12 和后面雷同 所以是 true
四、总结
正则中包含了很多的符号、类型、匹配范畴、匹配数量、匹配准则等等,像贪心、排除、向前援用等等,这些个应用办法其实也不难,只有依照正则的规范就能够组合出你想要匹配和拦挡进去的字符串内容信息。
五、系列举荐
- 握草,你居然在代码里下毒!
- 程序员为什么热衷于造轮子,升职加薪吗?
- BATJTMD,大厂招聘,都招什么样 Java 程序员?
- 工作 3 年,看啥材料能月薪 30K?
- 数学,离一个程序员有多近?
- abc ↩
- bc ↩
- m-p ↩
- 0-9 ↩
- \s ↩
- \w ↩
- \p{Lu} ↩
正文完