1、正则基础以及使用
正则的作用:用来检索、替换那些符合某个模式(规则)的文本。
1.1、基础字符
字符 | 描述 |
普通字符 | 没有特殊含义,就表示字符当前的含义,例如:/a/ -> 匹配字母 a |
\d | 匹配数字,相当于 [0-9] |
\D | 匹配非数字,相当于 [^0-9] |
\w | 匹配字母、数字、下划线。相当于 [a-zA-Z0-9_] |
\W | 匹配非字母、数字、下划线。相当于 [^a-zA-Z0-9_] |
\b | 匹配一个字边界,即字与空格间的位置 定位符 |
\B | 非字边界匹配 定位符 |
(?i) | 不区分大小写,例如a(?i)bc,匹配abc,其中bc不区分大小写 |
1.2、特殊字符
* + 都是贪婪模式,加上?可以转成非贪婪模式。
字符 | 描述 |
^ | 匹配输入字符串的开始位置 定位符,但是在方括号中[^],表示非的含义 |
$ | 匹配输入字符串的结尾位置 定位符。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r' |
. | 匹配除换行符 \n之外的任何单字符 |
* | 匹配前面的子表达式零次或多次 |
+ | 匹配前面的子表达式一次或多次 |
? | 匹配前面的子表达式零次或一次,或指明为非贪婪限定符 |
\ | 转译字符 |
| | 或 |
括号 | 小括号表示:捕获 中括号表示:匹配其中任意一个,例如[1-3],匹配1 2 3 大括号内加上数字表示需要匹配字符出现的次数,例如a{1,3},匹配 a 或者 aa 或者 aaa |
\1 | 反向引用,表示第一个捕获组的信息(捕获组按照左括号出现的顺序计算) 例如:(\\d)\\1,表示匹配两个数字 java中,如果在正则之外要使用,可以使用 $1, matcher.replaceAll("$1"); |
(?:exp) | 非捕获分组:作为一个组,但是不捕获组 (即括号内的内容只有限制效果,不作为字符串的一部分输出) ( 与断言一样,属于非捕获分组 ) |
1.3、断言
符号 | 名称 | 样例 |
reg(?=exp) | 零宽度正先行断言 | (?=\d{3}) 表示校验的位置后面是三个数字 |
reg(?!exp) | 零宽度负先行断言 | (?!\d{3}) 表示校验的位置后面不是三个数字 |
(?<=exp)reg | 零宽度正后行断言 | (?<=\d{3})表示校验的位置前面是三个数字 |
(?<!exp)reg | 零宽度负后行断言 | (?<!\d{3}) 表示校验的位置前面不是三个数字 |
1.4、非打印字符
字符 | 描述 |
\cx | 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\f | 匹配一个换页符。等价于 \x0c 和 \cL 。 |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ 。 |
\r | 匹配一个回车符。等价于 \x0d 和 \cM 。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v] 。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v] 。 |
\t | 匹配一个制表符。等价于 \x09 和 \cI 。 |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK 。 |
1.5、常用工具网址
描述 | 网址 |
在线正则验证网址1 | 这个网址点击左下角的Regex Debugger可以验证正则是不是可靠https://regex101.com/ |
在线正则验证网址2 | 正则表达式在线验证-在线正则表达式校验工具 |
W3C正则学习 | 正则表达式 – 语法_w3cschool |
菜鸟教程正则学习 | 正则表达式手册正则表达式 – 语法 | 菜鸟教程正则表达式手册 |
正则表达式手册 | 正则表达式手册 |
1.6、常用正则
内容 | 正则表达式 |
汉字 | https://regex101.com/^[\u4e00-\u9fa5]{0,}$https://regex101.com/ |
Email地址 | ^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ |
腾讯QQ号: (腾讯QQ号从10000开始) | [1-9][0-9]{4,} |
中国邮政编码 | [1-9]\d{5}(?!\d) |
域名 | [a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.? |
2、正则表达式实现原理
正则表达式的引擎有两个:DFA(确定性有穷自动机)和 NFA(非确定性有穷自动机)
java语言使用的NFA,因为其强大的功能,但是其复杂度不稳定,稳定性依赖于正则表达式本身,所以正则写不好,CPU高位告警。
正则实现的基本原理:
NFA匹配:(在语法解析的时候,构造出的一个有向图。然后通过深搜的方式,去一条路径一条路径的递归尝试。)
会根据正则表达式,一个一个字符匹配,比如将要匹配规则如下:
1、msg = "Sharp tools make good work.";
2、regex = "go{1,3}d";
匹配步骤;
1、取正则的第一个字符g,进行匹配;
2、匹配到good中的g;
3、取第二个字符o,进行匹配;
4、匹配到good中的第一个o;
5、因为o最多可匹配3次,继续匹配o;
6、匹配到good中的第二个o;
7、因为o最多可匹配3次,继续匹配o;
8、匹配到good中的d,不匹配,此时将发生回溯;
------回溯:匹配的数字d,将返回到字符中,继续接受匹配------
9、取正则的最后一个字符d,进行匹配;
10、匹配到good中的d;
11、再次取正则的字符,发现已经到末端,本次匹配结束。
回溯:在使用贪婪匹配或者惰性匹配或者或匹配进入到匹配路径选择的时候,遇到失败的匹配路径,尝试走另外一个匹配路径的这种行为。
注意:回溯对性能影响较大,应尽可能避免。可以在后面在增加一个 + ,转成独占模式,尽可能多的匹配但是不回溯。
正则可以转成状态转移图:
3、java正则使用
public class RegTheory {
public static void main(String[] args) {
String content = " 8、先前的人,只知道\"为儿孙作马牛\",固然是错误的,但只顾现在,不想将来" +
",\"任儿孙作马牛\",却不能不说是一个更大的错误。" +
"正在飙到8888.9999Km/h";
// 给分组取一个别名,这样matcher.group("g1")也可以获取到分组
// String reg = "(?<g1>\\d\\d)\\d\\d";
String reg = "\\d\\d\\d\\d";
Pattern pattern = Pattern.compile(reg); //创建正则表达式
Matcher matcher = pattern.matcher(content); //匹配对象
/**
* 源码解析
*
* matcher.find()
* 1、根据指定的规则,定位满足的字符串
* 2、将找到的索引记录到matcher对象的属性中
* groups[0] = 0, 将找的字符大的索引+1记录到groups[1]中
* 3、同时记录oldLast为子字符串末尾索引+1
*
* matcher.group()
* 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、根据groups[0]到groups[1]截取成子字符
*
* 重复上面的步骤
*/
while (matcher.find()) {
/**
* group()括号内的数字表示分组, 对应正则表达式中的捕获组
* 捕获组的索引依次记录:例如第一组,记录在groups[2]和groups[3]
* 其它分组依次类推...
*/
System.out.println(matcher.group(0));
}
}
参考:
本文引用网址如下:
https://www.w3cschool.cn/zhengzebiaodashi/regexp-syntax.html