Java正则表达式完全指南

Java正则表达式完全指南

正则表达式(Regular Expression,简称Regex)是一种强大的文本处理工具,它可以帮助开发者高效地进行字符串匹配、查找、替换和分割等操作。在Java中正则表达式的应用场景极为广泛,从简单的表单验证到复杂的文本解析,都离不开正则表达式的支持。虽然之前我也讲过正则表达式,但过于通用,今天本文将专门全面介绍Java中正则表达式的相关知识,从基础语法到高级应用,并结合丰富实例代码,带你深入理解和掌握这一强大工具。

一、正则表达式基础概念

1.1 什么是正则表达式

正则表达式是一种由字符和特殊符号组成的模式,用于描述字符串的特定格式规则。通过使用正则表达式,可以:

  • 检查字符串是否符合特定格式(如邮箱、手机号)
  • 从文本中提取感兴趣的内容(如URL、数字)
  • 替换文本中的特定部分
  • 将文本按特定规则分割

1.2 Java中的正则表达式支持

Java通过java.util.regex包提供对正则表达式的支持,主要涉及以下三个类:

  • Pattern:用于编译正则表达式,将正则表达式字符串编译为模式对象。
  • Matcher:用于执行匹配操作,对输入字符串进行解释和匹配操作。
  • PatternSyntaxException:用于处理正则表达式语法错误的异常类。

二、正则表达式基本语法

2.1 普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符,它们直接匹配自身。例如:

  • abc 匹配字符串 "abc"
  • 123 匹配字符串 "123"

2.2 元字符

元字符是正则表达式中具有特殊含义的字符,常用的元字符及其含义如下:

元字符 描述
. 匹配除换行符以外的任意字符
^ 匹配字符串的开始位置
$ 匹配字符串的结束位置
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
{n} 匹配前面的子表达式恰好n次
{n,} 匹配前面的子表达式至少n次
{n,m} 匹配前面的子表达式至少n次,至多m次
[] 匹配方括号中指定的任意一个字符
[^] 匹配不在方括号中指定的任意一个字符
() 标记一个子表达式的开始和结束位置
` `

2.3 预定义字符类

为了简化常用字符类的定义,Java提供了一些预定义字符类:

预定义字符类 等价表达式 描述
\d [0-9] 匹配一个数字字符
\D [^0-9] 匹配一个非数字字符
\w [a-zA-Z_0-9] 匹配一个单词字符(字母、数字、下划线)
\W [^a-zA-Z_0-9] 匹配一个非单词字符
\s [ \t\n\x0B\f\r] 匹配一个空白字符(空格、制表符、换行符等)
\S [^ \t\n\x0B\f\r] 匹配一个非空白字符

三、Java中正则表达式的基本用法

3.1 编译正则表达式

在Java中使用正则表达式,首先需要将正则表达式字符串编译为Pattern对象:

java 复制代码
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        // 编译正则表达式
        Pattern pattern = Pattern.compile("a.*c");
    }
}

3.2 创建Matcher对象并执行匹配

编译后的Pattern对象用于创建Matcher对象,然后通过Matcher对象执行匹配操作:

java 复制代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        // 编译正则表达式
        Pattern pattern = Pattern.compile("a.*c");
        
        // 创建Matcher对象
        Matcher matcher = pattern.matcher("abc");
        
        // 执行匹配操作
        boolean isMatch = matcher.matches();
        System.out.println("是否匹配: " + isMatch);  // 输出: true
    }
}

3.3 常用的Matcher方法

  • matches():尝试将整个输入序列与模式匹配。
  • find():在输入序列中查找下一个匹配的子序列。
  • group():返回当前匹配的子序列。
  • start():返回当前匹配的子序列的起始索引。
  • end():返回当前匹配的子序列的结束索引加1。

示例代码:

java 复制代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        String input = "Hello, world! Hello, Java!";
        Pattern pattern = Pattern.compile("Hello");
        Matcher matcher = pattern.matcher(input);
        
        // 查找所有匹配项
        while (matcher.find()) {
            System.out.println("匹配到: " + matcher.group() + 
                              ", 起始位置: " + matcher.start() + 
                              ", 结束位置: " + matcher.end());
        }
    }
}

输出结果:

java 复制代码
匹配到: Hello, 起始位置: 0, 结束位置: 5
匹配到: Hello, 起始位置: 14, 结束位置: 19

四、正则表达式高级应用

4.1 分组与捕获

使用圆括号()可以将正则表达式中的部分内容分组,每个分组可以被单独捕获和引用。分组编号从1开始,0表示整个匹配结果。

示例:匹配并提取邮箱地址中的用户名和域名

java 复制代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GroupExample {
    public static void main(String[] args) {
        String email = "test.user@example.com";
        String regex = "([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})";
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(email);
        
        if (matcher.matches()) {
            System.out.println("完整匹配: " + matcher.group(0));  // 整个匹配结果
            System.out.println("用户名: " + matcher.group(1));    // 第一组
            System.out.println("域名: " + matcher.group(2));      // 第二组
        }
    }
}

输出结果:

java 复制代码
完整匹配: test.user@example.com
用户名: test.user
域名: example.com

4.2 反向引用

在正则表达式中,可以使用\n(n为数字)引用前面已经捕获的分组。例如,匹配重复的单词:

java 复制代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BackreferenceExample {
    public static void main(String[] args) {
        String text = "hello hello world world";
        String regex = "\\b(\\w+)\\s+\\1\\b";
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        
        while (matcher.find()) {
            System.out.println("重复的单词: " + matcher.group());
        }
    }
}

输出结果:

java 复制代码
重复的单词: hello hello
重复的单词: world world

4.3 贪婪匹配与非贪婪匹配

  • 贪婪匹配 :默认情况下,正则表达式的量词(如*+{n,m})是贪婪的,会尽可能多地匹配字符。
  • 非贪婪匹配 :在量词后面加上?,可以将贪婪匹配转换为非贪婪匹配,尽可能少地匹配字符。

示例:

java 复制代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GreedyVsNonGreedy {
    public static void main(String[] args) {
        String text = "<html><body><h1>Hello</h1></body></html>";
        
        // 贪婪匹配
        String greedyRegex = "<.*>";
        Pattern greedyPattern = Pattern.compile(greedyRegex);
        Matcher greedyMatcher = greedyPattern.matcher(text);
        
        if (greedyMatcher.find()) {
            System.out.println("贪婪匹配: " + greedyMatcher.group());
        }
        
        // 非贪婪匹配
        String nonGreedyRegex = "<.*?>";
        Pattern nonGreedyPattern = Pattern.compile(nonGreedyRegex);
        Matcher nonGreedyMatcher = nonGreedyPattern.matcher(text);
        
        while (nonGreedyMatcher.find()) {
            System.out.println("非贪婪匹配: " + nonGreedyMatcher.group());
        }
    }
}

输出结果:

java 复制代码
贪婪匹配: <html><body><h1>Hello</h1></body></html>
非贪婪匹配: <html>
非贪婪匹配: <body>
非贪婪匹配: <h1>
非贪婪匹配: </h1>
非贪婪匹配: </body>
非贪婪匹配: </html>

4.4 零宽断言

零宽断言用于在特定位置匹配某些内容,但不包含匹配的内容本身。Java支持四种零宽断言:

断言类型 语法 描述
正向先行断言 (?=pattern) 匹配后面跟着pattern的位置
负向先行断言 (?!pattern) 匹配后面不跟着pattern的位置
正向后行断言 (?<=pattern) 匹配前面是pattern的位置
负向后行断言 (?<!pattern) 匹配前面不是pattern的位置

示例:匹配所有以"ing"结尾的单词

java 复制代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LookaroundExample {
    public static void main(String[] args) {
        String text = "running jumping swimming";
        String regex = "\\b\\w+(?=ing\\b)";
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        
        while (matcher.find()) {
            System.out.println("匹配到: " + matcher.group());
        }
    }
}

输出结果:

复制代码
匹配到: run
匹配到: jump
匹配到: swim

五、正则表达式在实际开发中的应用

5.1 表单验证

正则表达式常用于表单验证,确保用户输入的数据符合预期格式。

邮箱验证
java 复制代码
public static boolean isValidEmail(String email) {
    String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
    return Pattern.matches(regex, email);
}
手机号验证
java 复制代码
public static boolean isValidPhone(String phone) {
    String regex = "^1[3-9]\\d{9}$";
    return Pattern.matches(regex, phone);
}
身份证号码验证
java 复制代码
public static boolean isValidIdCard(String idCard) {
    String regex = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$";
    return Pattern.matches(regex, idCard);
}

5.2 文本替换

使用正则表达式可以方便地进行文本替换操作。

替换HTML标签
java 复制代码
public static String removeHtmlTags(String html) {
    String regex = "<[^>]+>";
    return html.replaceAll(regex, "");
}
敏感词过滤
java 复制代码
public static String filterSensitiveWords(String text) {
    String[] sensitiveWords = {"敏感词1", "敏感词2", "敏感词3"};
    String regex = String.join("|", sensitiveWords);
    return text.replaceAll(regex, "***");
}

5.3 文本分割

使用正则表达式可以按复杂规则分割文本。

按逗号或空格分割
java 复制代码
public static String[] splitText(String text) {
    String regex = "[,\\s]+";
    return text.split(regex);
}
按数字分割
java 复制代码
public static String[] splitByNumbers(String text) {
    String regex = "\\d+";
    return text.split(regex);
}

六、正则表达式性能优化

  1. 编译一次,多次使用 :避免在循环中重复编译相同的正则表达式,应将编译后的Pattern对象缓存并复用。
  2. 简化正则表达式:复杂的正则表达式会降低匹配效率,尽量使用简单、明确的表达式。
  3. 避免过度使用回溯:贪婪匹配和反向引用可能导致大量回溯,影响性能。
  4. 优先使用String类的方法 :对于简单的字符串操作,如startsWith()endsWith()indexOf()等,应优先使用String类的方法,比正则表达式效率更高。

七、常见问题与注意事项

  1. 转义字符问题 :在Java字符串中使用正则表达式时,需要注意转义字符。例如,匹配点号.需要写成\\.,匹配反斜杠\需要写成\\\\
  2. 性能问题:复杂的正则表达式可能导致性能问题,特别是在处理大量数据时。
  3. 边界问题 :使用^$时要注意是否需要匹配整个字符串,还是只需要匹配部分内容。
  4. Unicode支持:Java默认支持Unicode字符,但在处理非ASCII字符时需要特别注意。

总结

正则表达式是Java中强大的文本处理工具,掌握正则表达式的基本语法和Java中的使用方法,对于提高字符串处理效率和开发质量至关重要。本文从基础概念入手,详细介绍了正则表达式的语法、Java中的API使用、高级应用场景以及性能优化等方面的内容,希望你在今后熟练使用尽量掌握。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!

ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

相关推荐
坐吃山猪3 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫4 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao4 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区5 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT6 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy6 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss8 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续8 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0448 小时前
ReAct模式解读
java·ai