元字符
特殊单字符
. 任意字符,不包括换行

\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);
}