20220729 正则表达式
参考资料
相关工具
概述
-
正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为"元字符")组成的文字模式。
-
模式描述在搜索文本时要匹配的一个或多个字符串。
-
正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
语法
普通字符
字符 | 描述 |
---|---|
[ABC] |
匹配 [...] 中的所有字符 |
[^ABC] |
匹配除了 [...] 中字符的所有字符 |
[A-Z] |
[A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母 |
. |
匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\n\r] |
[\s\S] |
匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行 |
\w |
匹配字母、数字、下划线。等价于 [A-Za-z0-9_] |
非打印字符
下表列出了表示非打印字符的转义序列:
字符 | 描述 |
---|---|
\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] 。注意 Unicode 正则表达式会匹配全角空格符 |
\S |
匹配任何非空白字符。等价于 [^ \f\n\r\t\v] |
\t |
匹配一个制表符。等价于 \x09 和 \cI |
\v |
匹配一个垂直制表符。等价于 \x0b 和 \cK |
特殊字符
所谓特殊字符,就是一些有特殊含义的字符
若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符 \
放在它们前面。
特殊字符 | 描述 |
---|---|
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合 |
$ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r' | |
( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用 |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符 |
+ | 匹配前面的子表达式一次或多次 |
* | 匹配前面的子表达式零次或多次 |
. | 匹配除换行符 \n 之外的任何单字符 |
[ | 标记一个中括号表达式的开始 |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符 |
{ | 标记限定符表达式的开始 |
| | 指明两项之间的一个选择 |
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配
字符 | 描述 |
---|---|
* | 匹配前面的子表达式零次或多次 |
+ | 匹配前面的子表达式一次或多次 |
? | 匹配前面的子表达式零次或一次 |
n 是一个非负整数。匹配确定的 n 次 | |
n 是一个非负整数。至少匹配 n 次。o{1,} 等价于 o+。o{0,} 则等价于 o* | |
m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。o{0,1} 等价于 o?。请注意在逗号和两个数之间不能有空格。 |
贪婪模式
*** 和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。**
定位符
字符 | 描述 |
---|---|
^ | 匹配输入字符串开始的位置 |
$ | 匹配输入字符串结尾的位置 |
\b | 匹配一个单词边界,即字与空格间的位置 |
\B | 非单词边界匹配 |
选择
用圆括号 ()
将所有选择项括起来,相邻的选择项之间用 |
分隔。
() 表示捕获分组,() 会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(n 是一个数字,表示第 n 个捕获组的内容)。
但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?:
放在第一个选项前来消除这种副作用。
表达式 | 描述 |
---|---|
exp1(?=exp2) | 查找 exp2 前面的 exp1 |
(?<=exp2)exp1 | 查找 exp2 后面的 exp1 |
exp1(?!exp2) | 查找后面不是 exp2 的 exp1 |
(?<!exp2)exp1 | 查找前面不是 exp2 的 exp1 |
反向引用
到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 \n
访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
可以使用非捕获元字符 ?:
、?=
或 ?!
来重写捕获,忽略对相关匹配的保存。
反向引用的最简单的、最有用的应用之一,是提供查找文本中两个相同的相邻单词的匹配项的能力
修饰符(标记)
标记也称为修饰符,正则表达式的标记用于指定额外的匹配策略。
标记不写在正则表达式里,标记位于表达式之外,格式如下:
/pattern/flags
修饰符 | 含义 | 描述 |
---|---|---|
i | ignore - 不区分大小写 | 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。 |
g | global - 全局匹配 | 查找所有的匹配项。 |
m | multi line - 多行匹配 | 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。 |
s | 特殊字符圆点 . 中包含换行符 \n | 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 |
元字符
运算符优先级
下表 从高到低 说明了各种正则表达式运算符的优先级顺序:
运算符 | 描述 |
---|---|
\ | 转义符 |
() , (?:) , (?=) , [] |
圆括号和方括号 |
*, +, ?, {n}, {n,}, | 限定符 |
^, $, \任何元字符、任何字符 | 定位点和序列(即:位置和顺序) |
| | 替换,"或"操作 字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food"。若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood" |
Java 正则表达式
JDK 用法
简单用法,匹配单组:
@Test
public void test0() {
String input = "abcd";
String regex = "abc+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
System.out.println(matcher.matches()); // false需要完全匹配,abcd可以匹配表达式abc.
matcher.reset();
while (matcher.find()) {
String group = matcher.group();
System.out.println(group); // abc
}
}
匹配多组:
@Test
public void test1() {
String regex = "([1-9])([a-z]+)";
String content = "123456runoob123runoob456";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group());
}
/*
6runoob
3runoob
*/
}
命名捕获组:
@Test
public void test3() {
String input = "2017-04-25";
String regex = "(?<year>\\d{4})-(?<md>(?<month>\\d{2})-(?<date>\\d{2}))";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
matcher.find();
System.out.print("\n===========使用名称获取=============");
System.out.printf("\nmatcher.group(0) value:%s", matcher.group(0));
System.out.printf("\n matcher.group('year') value:%s", matcher.group("year"));
System.out.printf("\nmatcher.group('md') value:%s", matcher.group("md"));
System.out.printf("\nmatcher.group('month') value:%s", matcher.group("month"));
System.out.printf("\nmatcher.group('date') value:%s", matcher.group("date"));
matcher.reset();
System.out.print("\n===========使用编号获取=============");
matcher.find();
System.out.printf("\nmatcher.group(0) value:%s", matcher.group(0));
System.out.printf("\nmatcher.group(1) value:%s", matcher.group(1));
System.out.printf("\nmatcher.group(2) value:%s", matcher.group(2));
System.out.printf("\nmatcher.group(3) value:%s", matcher.group(3));
System.out.printf("\nmatcher.group(4) value:%s", matcher.group(4));
}
Hutool 用法
@Test
public void test11() {
String regex = "([1-9])([a-z]+)";
String content = "123456runoob123runoob456";
boolean match = ReUtil.isMatch(regex, content);
System.out.println(match); // false
// findAll 可以获取到多个匹配中的单个组
List<String> all = ReUtil.findAll(regex, content, 0);
System.out.println(all); // [6runoob, 3runoob]
// getAllGroups 可以获取到第一个匹配中的多个组
List<String> allGroups = ReUtil.getAllGroups(Pattern.compile(regex), content);
System.out.println(allGroups); // [6runoob, 6, runoob]
// extractMulti 组合第一个匹配中的组
String s = ReUtil.extractMulti(regex, content, "$1--$2");
System.out.println(s); // 6--runoob
// delFirst 删除第一个匹配到的内容
String resultDelFirst = ReUtil.delFirst(regex, content);
System.out.println(resultDelFirst); // 12345123runoob456
}