正则表达式

2025/9/30 Java

正则表达式 (opens new window)(Regular Expression,简称 Regex 或 RegExp)是一种用来匹配字符串中字符组合的模式。

正则表达式是一种用于模式匹配和搜索文本的工具。

正则表达式提供了一种灵活且强大的方式来查找、替换、验证和提取文本数据。

正则表达式可以应用于各种编程语言和文本处理工具中,如 JavaScript、Python、Java、Perl 等。

# Metacharacters

元字符 (opens new window)(Metacharacters):在正则表达式中有特殊含义的字符。下面是一些常见的正则表达式元字符:

符合 含义 示例
基本元字符 **点号.**‌ 匹配除换行符之外的任何单个字符。 a.c 匹配"abc"、"a#c"、"a1c"等
基本元字符 **反斜杠\**‌ 转义字符,用于转义元字符, 使后面的字符失去特殊含义,使其表示其本身。 \. 匹配实际的点号字符"."
基本元字符 **脱字符^**‌ 定位符。当在方括号表达式外时,表示行的开始;
否定字符类。在方括号表达式内,表示否定或排除。
^abc 匹配以"abc"开始的字符串
[^a] 匹配非"a"的任意字符
基本元字符 **美元符号$**‌ 定位符。表示行的结束。 abc$ 匹配以"abc"结束的字符串
字符类元字符 方括号[] 定义字符集合,匹配其中任意一个字符 [aeiou] 匹配任意一个元音字母
字符类元字符 否定字符类[^] 匹配不在方括号中的任意字符 [^0-9] 匹配任意非数字字符
字符类元字符 连字符- 在字符类中表示范围 [a-z] 匹配任意小写字母
量词元字符 问号? 限定符。匹配前面的子表达式零次或一次。默认情况下,量词(*, +, ?, {})是贪婪的,会尽可能多地匹配字符。在量词后加?可使其变为非贪婪(懒惰)模式。 ab?c 匹配"ac"和"abc"
量词元字符 星号* 限定符。匹配前面的子表达式零次或多次。 ab*c" 匹配"ac"、"abc"、"abbc"、"abbbc"等
量词元字符 ‌**加号+**‌ 限定符。匹配前面的子表达式一次或多次。 ab+c 匹配"abc"、"abbc"、"abbbc"等,但不匹配"ac"
量词元字符 花括号{n} 限定符。精确匹配n次。 a{3} 匹配 "aaa"
量词元字符 {n,} 限定符。至少匹配n次。 a{2,} 匹配 "aa", "aaa" 等
量词元字符 {n,m} 限定符。匹配n到m次。 a{2,4} 匹配 "aa", "aaa", "aaaa"
贪婪与非贪婪量词 *? 限定符。零次或多次,但尽可能少。
默认情况下,量词(*, +, ?, {})是贪婪的,会尽可能多地匹配字符。在量词后加?可使其变为非贪婪(懒惰)模式。
<.*?> 匹配HTML标签时不会跨标签匹配
贪婪与非贪婪量词 +? 限定符。一次或多次,但尽可能少
贪婪与非贪婪量词 ?? 限定符。零次或一次,但尽可能少
贪婪与非贪婪量词 {n,m}? 限定符。n到m次,但尽可能少
分组元字符 (pattern) 定义子表达式或捕获组。
捕获分组元字符,非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号。
(ab)+ 匹配 "ab", "abab" 等
分组元字符 (?<name>pattern) 捕获分组元字符,命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号
替代尖括号,例如(?'name')
非捕获分组元字符 (?:pattern) 一个非捕获分组(Non-capturing group),它用于对正则表达式进行分组,但不会创建反向引用。这种分组方式比普通的捕获分组。它的特点和用法:
1. 提高性能 - 不需要存储匹配的内容
2. 代码更清晰 - 明确表示这个组不需要被引用
3. 减少内存使用 - 不会创建额外的捕获组
(?:jpg|jpeg|png) 匹配 jpg 或 jpeg 或 png 文件扩展名
非捕获分组元字符 (?=pattern) 正向肯定预查。匹配后面跟着特定模式的位置。一种前瞻性断言(Positive Lookahead)的非捕获分组,它用于匹配某个位置,这个位置的后面必须匹配指定的pattern,但不会消耗这些字符。它的特点和用法:
1. 匹配一个位置,这个位置后面的字符要满足pattern
2. 不会将pattern部分包含在最终的匹配结果中
3. 不会消耗字符,只是检查是否满足条件
Windows(?=95|98|NT|2000) 匹配"Windows2000"中的“Windows",但不匹配“Windows3.1"中的"Windows"。
非捕获分组元字符 (?!pattern) 正向否定预查。匹配后面不跟着特定模式的位置。一种负向前瞻断言(Negative Lookahead),它用于匹配某个位置,这个位置的后面不能匹配指定的pattern。这是一个非捕获分组,不会消耗任何字符。它的特点和用法:
1. 匹配一个位置,这个位置后面的字符不能满足pattern
2. 不会消耗字符,只是检查条件
3. 不会捕获匹配内容
Windows(?!95|98|NT|2000) 匹配"Windows3.1"中的“Windows",但不匹配“Windows2000"中的"Windows"。
非捕获分组元字符 (?<=pattern) 反向肯定预查。匹配前面是特定模式的位置。 (?<=95|98)Windows 匹配前面是95或98的"Windows"
非捕获分组元字符 (?<!pattern) 反向否定预查。匹配前面不是特定模式的位置。 (?<!95|98)Windows 匹配前面不是95或98的"Windows"
反向引用 反斜杠加数字(例如,\1 用于引用捕获组的语法。
反向引用(Backreference)允许我们在正则表达式中(模式内)引用之前捕获组匹配到的内容。
它使用 \1 \2 等来引用前面的捕获组。例如:\1 引用第一个捕获组,\2 引用第二个捕获组,以此类推。
反向引用是正则表达式中一个强大的功能,它允许我们匹配重复的内容或验证对称的模式。在处理HTML/XML标签、查找重复内容、验证输入等场景中特别有用。使用时需要注意捕获组的编号和作用范围,以及非捕获组不能被引用的限制。
\1 的用法:
1. 用在正则表达式模式内部
2. 用于匹配与第一个捕获组相同的内容
$1 的用法:
1. 用在替换字符串中
2. 用于引用正则表达式中捕获的组
(ab)c\\1 匹配"abcab"
选择元字符 **竖线|**‌ 表示"或"关系,用于分隔两个或多个可能的匹配模式。 cat|dog 匹配 "cat" 或 "dog"
特殊字符类元字符 \d 匹配任意数字,等价于 [0-9]
特殊字符类元字符 \D 匹配任意非数字,等价于 [^0-9]
特殊字符类元字符 \w 匹配任意单词字符(字母、数字、下划线),等价于 [a-zA-Z0-9_]
特殊字符类元字符 \W 匹配任意非单词字符,等价于 [^a-zA-Z0-9_]
特殊字符类元字符 \s 匹配任意空白字符(空格、制表符、换行符等)
特殊字符类元字符 \S 匹配任意非空白字符
边界匹配元字符 \b 定位符。匹配单词边界 \bcat\b 匹配 "cat" 但不匹配 "category"
边界匹配元字符 \B 定位符。匹配非单词边界 \Bcat\B 匹配 "scattered" 中的 "cat" 但不匹配单独的 "cat"
其他元字符 \n 匹配换行符
其他元字符 \t 匹配制表符
其他元字符 \r 匹配回车符
其他元字符 \f 匹配换页符
其他元字符 \v 匹配垂直制表符

# Pattern and Matcher in Java

Java正则表达式主要涉及 PatternMatcher 两个核心类,它们都在 java.util.regex 包中。

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class PatternDemo {
    public static void demonstratePattern() {
        // 1. 编译正则表达式
        Pattern pattern = Pattern.compile("\\w+");
        
        // 2. 编译时设置标志
        Pattern patternWithFlags = Pattern.compile("\\w+", 
            Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        
        // 3. 创建Matcher对象
        Matcher matcher = pattern.matcher("test string");
        
        // 4. 分割字符串
        String[] parts = pattern.split("hello,world");
        
        // 5. 快速匹配
        boolean isMatch = Pattern.matches("\\d+", "123");
        
        // 6. 获取正则表达式
        String regex = pattern.pattern();
        
        // 7. 获取编译标志
        int flags = pattern.flags();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MatcherDemo {
    public static void demonstrateMatcher() {
        Pattern pattern = Pattern.compile("\\w+");
        String text = "Hello World";
        Matcher matcher = pattern.matcher(text);
        
        // 1. 查找匹配
        boolean found = matcher.find();      // 查找下一个匹配
        boolean matches = matcher.matches(); // 整个字符串是否匹配
        boolean lookingAt = matcher.lookingAt(); // 从开始处查找匹配
        
        // 2. 获取匹配信息
        int start = matcher.start();     // 匹配的起始位置
        int end = matcher.end();         // 匹配的结束位置
        String group = matcher.group();   // 匹配的内容
        
        // 3. 替换操作
        String replaced = matcher.replaceAll("replacement");
        String replacedFirst = matcher.replaceFirst("replacement");
        
        // 4. 重置操作
        matcher.reset();             // 重置匹配器
        matcher.reset("new text");   // 重置并更改文本
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Pattern 类核心方法:

  • compile(String regex): 编译正则表达式
  • compile(String regex, int flags): 带标志的编译
  • matcher(CharSequence input): 创建Matcher对象
  • split(CharSequence input): 分割字符串
  • matches(String regex, CharSequence input): 静态匹配方法
  • pattern(): 返回正则表达式字符串
  • flags(): 返回编译标志

Matcher 类核心方法:

  • find(): 查找下一个匹配
  • matches(): 整个字符串匹配
  • lookingAt(): 从开始处匹配
  • group(): 返回匹配的字符串
  • group(int group): 返回指定分组
  • start(): 返回匹配的起始位置
  • end(): 返回匹配的结束位置
  • replaceAll(String replacement): 替换所有匹配
  • replaceFirst(String replacement): 替换第一个匹配
  • reset(): 重置匹配器
  • appendReplacement(): 追加替换内容
  • appendTail(): 追加剩余内容

在Java中,正则表达式中实现不区分大小写匹配的示例:

// 使用 (?i) 标记
String regex = "(?i)hello";

// 或者使用 Pattern.CASE_INSENSITIVE 标志
Pattern pattern = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);
1
2
3
4
5

部分模式不区分大小写:

// (?i)abc 表示abc都不区分大小写
// a(?i)bc 表示bc不区分大小写
// a((?i)b)c 表示只有b不区分大小写

String text = "UserName: John_Doe, EMAIL: John@example.com";

// 只有 email 部分不区分大小写
String pattern1 = "Username: \\w+, (?i)email: [\\w@.]+";
Pattern.matches(pattern1, text);  // true

// 也可以用 (?-i) 关闭不区分大小写
String pattern2 = "(?i)Username: \\w+, email: (?-i)[a-z@.]+";
1
2
3
4
5
6
7
8
9
10
11
12

注意事项:

  1. (?i) 可以放在整个正则表达式的开始处,也可以放在需要忽略大小写的部分前面

  2. 可以使用 (?-i) 来关闭不区分大小写的效果