元字符
特殊单字符
.
任意字符,不包括换行
\d
任意的数字
\D
任意的非数字
\w
任意的字母、数字、下划线
\W
非字母、数字、下划线
\s
空白符号
\S
非空白符号
空白符号
-
\r
回车 -
\n
换行 -
\f
换页 -
\t
制表符 -
\v
垂直制表符 -
\s
任意空白符\s
能匹配上各种空白符号,也可以匹配上空格。换行有专门的表示方式,在正则中,空格就是用普通的字符英文的空格来表示。关于这些空白符号的说明 回车表示,光标移动到行首 换行表示光标移动到下一行,左右位置是和上一行一样的。
范围
- | 或 ab|bc 表示 ab 或者 gc
- […],[a-z],[0-9] 括号中的任意字符
- [^…] 取反,不能是括号中的字符。
管道范围匹配的例子
量词
*
表示 0 到多次
+
表示 1 到多次
?
表示 0 到一次
{m}
出现 m 次
{m,}
至少出现 m 次
{m,n}
表示 m 到 n 次
断言
单词的边界
使用\b
设置单词的边界
行首和行尾
^
:匹配字符串的开头
$
:匹配字符串的结尾
环视
(?=exp)
:正向
(?!exp)
:正向逆
(?<=exp)
:反向
(?<!exp)
:反向逆
匹配模式
贪婪模式
尽可能多地匹配字符,正则表达式默认就是贪婪模式的。
非贪婪模式
量词后面加上?
,找出满足条件的最小的字符串。
独占模式(possessive)
https://www.regular-expressions.info/possessive.html
避免匹配的时候的回溯,贪婪模式和非贪婪模式都是支持回溯的,例如如下的例子:
匹配规则
大小写不敏感
/**
* 在正则表达式字符串中设置大小写不敏感
*/
@Test
public void testMatchInsensitiveInNativeRegString() {
String string = "cat CAT cat";
String regString = "(?i)(cat\s?)+";
assertThat(string.matches(regString)).isEqualTo(true);
}
/**
* 在匹配模式中设置大小写不敏感
*/
@Test
public void testMatchInsensitiveInPatternMode() {
String testString = "cat CAT cat";
String testReg = "(cat\\s?)+";
Pattern pattern = Pattern.compile(testReg, Pattern.CASE_INSENSITIVE);
assertThat(pattern.matcher(testString).matches()).isEqualTo(true);
}
单行匹配(SingleLine)模式
默认的情况下 .
会匹配所有非换行字符,
可以使用 (?s)
启动单行模式,这样 .
会匹配所有的字符。
多行匹配(Multiline)模式
通常情况下 ^
匹配字符串的开头,$
匹配字符串的结尾。
加上
(?m)
可以变为多行模式
多行模式 ^
匹配行首 ,$
匹配行尾。
/**
* Java 中的多行模式
*/
@Test
public void matchInMultilineMode() {
String testString = "Hello ya world\r\nHello ya world";
String testReg = "^Hello|world$";
Pattern pattern = Pattern.compile(testReg, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(testString);
List<String> results = matcher.results()
.map(MatchResult::group).toList();
assertThat(results.size()).isEqualTo(4);
assertThat(results.get(0)).isEqualTo("Hello");
assertThat(results.get(1)).isEqualTo("world");
assertThat(results.get(2)).isEqualTo("Hello");
assertThat(results.get(3)).isEqualTo("world");
}
注释模式
正则表达式中也可以写注释
这样的写法有的编程语言式不识别的,一般会提供一个
x
模式来识别注释,例如,Java 中就不识别上面的那个正则表达式,可以使用 (?x)
来开启。
多行的情况,正则表达式里面的换行会被忽略
其他模式
除此之外,有的编程语言中(例如 java)中还提供了其他的模式,可在编程的时候查看其使用文档
分组和引用
简单的分组
括号在正则表达式中可以用来分组,被括号括起来的子表达式会被保存为一个分组。 分组会有分组的编号,简单地说第几个括号就是第几个分组。
不保存分组
在括号里面的默认会保存为分组,如果不需要分组只需要在括号里面使用 ?:
,不保存分组会提高性能。
例如:
可以看到,上面的例子只保存了一个分组
嵌套的分组
多个括号嵌套的情况下怎么看分组? 看左括号是第几个,就可以确认是第几个分组
命名的分组
分组默认是可以根据序号获得的,分组的编号可能会发生变化,不过也可以为分组指定名字,分组名的格式为?P<groupname>
,注意这个不同的语言不一样,其中 java 的是 ?<groupName>
。
@Test
public void testNamedGroupMatch() {
String string = "2022-05-03 17:55:00";
String regString = "(\\d{4}-\\d{2}-\\d{2}) (?<time>\\d{2}:\\d{2}:\\d{2})";
Pattern pattern = Pattern.compile(regString);
Matcher matcher = pattern.matcher(string);
boolean matches = matcher.matches();
assertThat(matches).isEqualTo(true);
if (matches) {
String time = matcher.group("time");
assertThat(time).isEqualTo("17:55:00");
String date = matcher.group(1);
assertThat(date).isEqualTo("2022-05-03");
}
}
分组引用
@Test
public void testReplaceByRegGroup() {
//java分组替换的例子
String string = "2022-05-03 17:55:00";
String regString = "((\\d{4})-(\\d{2})-(\\d{2})) ((\\d{2}):(\\d{2}):(\\d{2}))";
String result = string.replaceAll(regString, "$2年$3月$4日 $6时$7分$8秒");
assertThat(result).isEqualTo("2022年05月03日 17时55分00秒");
log.info(result);
}