正则表达式详解

文章目录

    • 1. 正则表达式基础
      • 1.1 什么是正则表达式
      • 1.2 为什么需要学习正则表达式
      • 1.3 Java中的正则表达式支持
    • 2. 正则表达式语法
      • 2.1 基本匹配
      • 2.2 元字符
        • 2.2.1 常用元字符
        • 2.2.2 转义字符
        • 2.2.3 字符类
        • 2.2.4 预定义字符类
        • 2.2.5 量词
      • 2.3 贪婪与非贪婪匹配
      • 2.4 分组与捕获
        • 2.4.1 命名分组
        • 2.4.2 非捕获分组
      • 2.5 边界匹配器
    • 3. 高级正则表达式特性
      • 3.1 零宽断言(Look-around)
      • 3.2 条件匹配
      • 3.3 模式标志
      • 3.4 反向引用
      • 3.5 递归匹配
    • 4. Java中的正则表达式API
      • 4.1 使用Pattern和Matcher类
      • 4.2 重要的Pattern方法
      • 4.3 重要的Matcher方法
      • 4.4 String类中的正则表达式方法
      • 4.5 特殊替换序列
    • 5. 常见正则表达式示例与解析
      • 5.1 数据验证正则表达式
        • 电子邮件验证
        • 电话号码验证(美国格式)
        • URL验证
        • 日期验证(YYYY-MM-DD格式)
        • 密码强度验证
      • 5.2 文本处理正则表达式
        • 提取所有HTML标签
        • 提取所有URL
        • 删除HTML标签
        • 格式化电话号码
      • 5.3 高级示例
        • 解析CSV数据
        • 提取文本中的所有日期
    • 6. 正则表达式在Java中的实际应用
      • 6.1 处理配置文件
      • 6.2 日志解析
      • 6.3 网页爬虫
      • 6.4 表单验证
      • 6.5 文本替换处理
    • 7. 正则表达式性能优化
      • 7.1 常见性能问题
      • 7.2 优化技巧
        • 7.2.1 缓存编译后的Pattern对象
        • 7.2.2 使用非捕获组
        • 7.2.3 优化量词
        • 7.2.4 使用原子组
        • 7.2.5 避免过度使用点通配符和贪婪量词
      • 7.3 性能测试
    • 8. 正则表达式调试与故障排除
      • 8.1 常见问题与解决方案
        • 8.1.1 匹配失败
        • 8.1.2 性能问题
      • 8.2 正则表达式可视化工具
      • 8.3 单元测试正则表达式
    • 9. 正则表达式的局限性与替代方案
      • 9.1 何时不使用正则表达式
      • 9.2 替代方案
        • 9.2.1 专用解析器
        • 9.2.2 字符串处理库
        • 9.2.3 状态机
    • 10. 总结与最佳实践
      • 10.1 正则表达式的核心概念回顾
      • 10.2 正则表达式最佳实践
      • 10.3 持续学习资源
    • 附录:正则表达式速查表
      • 元字符
      • 字符类
      • 预定义字符类
      • 量词
      • 零宽断言
      • 其他

1. 正则表达式基础

1.1 什么是正则表达式

正则表达式(Regular Expression,简称RegEx)是一种用于描述字符串匹配模式的表达式语言,是一种强大的文本处理工具。通过使用特定的语法规则,我们可以:

  • 在文本中查找特定的模式
  • 验证字符串是否符合特定格式(如邮箱、电话号码等)
  • 替换文本中的特定内容
  • 从文本中提取特定的信息

正则表达式被广泛应用于文本处理、数据验证、爬虫开发、日志分析等场景,是程序员必备的技能之一。

1.2 为什么需要学习正则表达式

假设你需要验证用户输入的是否是有效的电子邮件地址。不使用正则表达式,你可能需要编写很多复杂的条件判断代码:

boolean isValidEmail(String email) {// 检查是否包含@符号if (!email.contains("@")) return false;// 检查@前后是否有内容String[] parts = email.split("@");if (parts.length != 2) return false;if (parts[0].length() == 0 || parts[1].length() == 0) return false;// 检查域名部分是否包含至少一个点if (!parts[1].contains(".")) return false;// 检查用户名部分是否有效(不包含特殊字符等)// ...更多复杂的检查return true;
}

而使用正则表达式,只需一行代码:

boolean isValidEmail(String email) {return email.matches("^[\\w.-]+@([\\w-]+\\.)+[\\w-]{2,4}$");
}

正则表达式不仅使代码更简洁,而且通常比手写的字符串处理逻辑更高效、更可靠。

1.3 Java中的正则表达式支持

Java通过java.util.regex包提供了对正则表达式的支持,主要包含以下核心类:

  • Pattern:编译后的正则表达式对象
  • Matcher:执行匹配操作的引擎
  • PatternSyntaxException:表示正则表达式语法错误的异常

此外,String类也提供了几个直接使用正则表达式的方法:

  • matches():判断字符串是否匹配正则表达式
  • split():使用正则表达式分割字符串
  • replaceAll()replaceFirst():使用正则表达式替换字符串内容

2. 正则表达式语法

2.1 基本匹配

最简单的正则表达式就是普通字符,它们表示字面值匹配:

String text = "Hello, World!";
boolean matches = text.matches("Hello, World!"); // 返回true

这里的正则表达式"Hello, World!"只匹配完全相同的字符串。

2.2 元字符

元字符是正则表达式中具有特殊含义的字符,是正则表达式强大功能的基础。

2.2.1 常用元字符
元字符描述示例
.匹配任意单个字符(除了换行符)"a.c" 匹配 “abc”, “adc”, “a1c” 等
^匹配字符串的开始"^Hello" 匹配以Hello开头的字符串
$匹配字符串的结束"World$" 匹配以World结尾的字符串
*匹配前面的表达式0次或多次"a*" 匹配 “”, “a”, “aa”, “aaa” 等
+匹配前面的表达式1次或多次"a+" 匹配 “a”, “aa”, “aaa” 等
?匹配前面的表达式0次或1次"colou?r" 匹配 “color” 和 “colour”
\转义字符"\\." 匹配 “.” 字符本身

Java示例:

// 匹配任意字符
String text = "cat";
boolean matches = text.matches("c.t"); // 返回true,因为"cat"符合"c任意字符t"的模式// 匹配开头和结尾
String text1 = "Hello, World!";
boolean startsWithHello = text1.matches("^Hello.*"); // 返回true,因为字符串以Hello开头
boolean endsWithWorld = text1.matches(".*World!$"); // 返回true,因为字符串以World!结尾// 使用量词
String text2 = "abbbc";
boolean matches2 = text2.matches("ab*c"); // 返回true,因为"abbbc"包含"a"后跟多个"b"再跟"c"
2.2.2 转义字符

在Java字符串和正则表达式中,反斜杠\都是特殊字符,因此在Java代码中表示正则表达式的反斜杠时,需要使用两个反斜杠\\

// 匹配字面上的点号(.)
String text = "example.com";
boolean matches = text.matches("example\\.com"); // 错误:在Java字符串中,\会被解释为转义字符
boolean correctMatches = text.matches("example\\\\.com"); // 错误:过多的反斜杠
boolean properMatches = text.matches("example\\.com"); // 正确:两个反斜杠表示正则表达式中的一个反斜杠
2.2.3 字符类

字符类允许匹配一组字符中的任意一个:

字符类描述示例
[abc]匹配方括号内的任意一个字符[abc] 匹配 “a”, “b”, 或 “c”
[^abc]匹配除了方括号内的任意一个字符[^abc] 匹配任何除了 “a”, “b”, 或 “c” 的字符
[a-z]匹配指定范围内的任意一个字符[a-z] 匹配任何小写字母
[a-zA-Z]可以组合多个范围[a-zA-Z] 匹配任何英文字母

Java示例:

// 匹配字符集中的任意字符
String text = "cat";
boolean matches = text.matches("[bcd]at"); // 返回false,因为"cat"的第一个字符不在[bcd]中
text = "bat";
matches = text.matches("[bcd]at"); // 返回true// 匹配字符范围
String digit = "5";
boolean isDigit = digit.matches("[0-9]"); // 返回true,因为"5"在数字范围内// 匹配多个范围
String letter = "K";
boolean isLetter = letter.matches("[a-zA-Z]"); // 返回true,因为"K"是字母
2.2.4 预定义字符类

为了方便,正则表达式提供了一些预定义的字符类:

预定义类描述等价于
\d匹配任意数字[0-9]
\D匹配任意非数字[^0-9]
\w匹配任意字母、数字或下划线[a-zA-Z0-9_]
\W匹配任意非字母、数字或下划线[^a-zA-Z0-9_]
\s匹配任意空白字符[ \t\n\r\f]
\S匹配任意非空白字符[^ \t\n\r\f]

在Java中使用这些预定义类时,需要使用双反斜杠:

// 检查是否为单个数字
String text = "7";
boolean isDigit = text.matches("\\d"); // 返回true// 检查是否由字母、数字和下划线组成
String username = "user_123";
boolean isValidUsername = username.matches("\\w+"); // 返回true// 检查是否包含空白字符
String hasSpace = "Hello World";
boolean containsSpace = hasSpace.matches(".*\\s.*"); // 返回true
2.2.5 量词

量词用于指定前面的表达式应该匹配多少次:

量词描述示例
*匹配0次或多次a* 匹配 “”, “a”, “aa”, …
+匹配1次或多次a+ 匹配 “a”, “aa”, …
?匹配0次或1次a? 匹配 “” 或 “a”
{n}精确匹配n次a{3} 匹配 “aaa”
{n,}匹配至少n次a{2,} 匹配 “aa”, “aaa”, …
{n,m}匹配n到m次a{1,3} 匹配 “a”, “aa”, “aaa”

Java示例:

// 精确匹配次数
String text = "aaa";
boolean matches = text.matches("a{3}"); // 返回true,因为正好有3个a// 匹配范围次数
String numbers = "123";
boolean isThreeDigits = numbers.matches("\\d{1,3}"); // 返回true,因为有1到3个数字// 组合使用
String phoneNumber = "123-456-7890";
boolean isValidPhone = phoneNumber.matches("\\d{3}-\\d{3}-\\d{4}"); // 返回true

2.3 贪婪与非贪婪匹配

默认情况下,量词是贪婪的,意味着它们会尽可能多地匹配字符:

String text = "abcdefg";
String pattern = "a.*f"; // 贪婪匹配
// 结果将匹配"abcdef",因为.*会尽可能多地匹配

可以通过在量词后添加?使其变成非贪婪(懒惰)匹配:

String text = "abcdefg";
String pattern = "a.*?f"; // 非贪婪匹配
// 结果将匹配尽可能少的字符,只要能匹配到f就停止

Java示例:

String html = "<div>内容1</div><div>内容2</div>";// 贪婪匹配:匹配从第一个<div>到最后一个</div>的所有内容
Pattern greedyPattern = Pattern.compile("<div>.*</div>");
Matcher greedyMatcher = greedyPattern.matcher(html);
if (greedyMatcher.find()) {System.out.println("贪婪匹配: " + greedyMatcher.group()); // 输出:<div>内容1</div><div>内容2</div>
}// 非贪婪匹配:匹配尽可能短的内容
Pattern lazyPattern = Pattern.compile("<div>.*?</div>");
Matcher lazyMatcher = lazyPattern.matcher(html);
if (lazyMatcher.find()) {System.out.println("非贪婪匹配: " + lazyMatcher.group()); // 输出:<div>内容1</div>
}

2.4 分组与捕获

括号()在正则表达式中用于分组,这不仅可以应用量词到整个组,还可以捕获匹配的文本:

String text = "John Smith";
Pattern pattern = Pattern.compile("(\\w+)\\s(\\w+)");
Matcher matcher = pattern.matcher(text);if (matcher.matches()) {String firstName = matcher.group(1); // "John"String lastName = matcher.group(2);  // "Smith"System.out.println("First name: " + firstName);System.out.println("Last name: " + lastName);
}
2.4.1 命名分组

Java 7及以上版本支持命名捕获组,使代码更具可读性:

String text = "John Smith";
Pattern pattern = Pattern.compile("(?<firstName>\\w+)\\s(?<lastName>\\w+)");
Matcher matcher = pattern.matcher(text);if (matcher.matches()) {String firstName = matcher.group("firstName"); // "John"String lastName = matcher.group("lastName");   // "Smith"System.out.println("First name: " + firstName);System.out.println("Last name: " + lastName);
}
2.4.2 非捕获分组

如果只想使用括号进行分组,但不需要捕获匹配的文本,可以使用非捕获分组(?:...)

String text = "abc123def456";
Pattern pattern = Pattern.compile("(?:\\d+)([a-z]+)");
Matcher matcher = pattern.matcher(text);if (matcher.find()) {// matcher.group(1)将是"def",不会捕获数字部分System.out.println(matcher.group(1));
}

2.5 边界匹配器

边界匹配器不匹配实际字符,而是匹配位置:

边界描述
^匹配行的开始
$匹配行的结束
\b匹配单词边界(一个\w与一个\W之间的位置,或字符串的开始或结束)
\B匹配非单词边界

Java示例:

// 匹配整个单词
String text = "This is a simple example";
Pattern pattern = Pattern.compile("\\bsimple\\b");
Matcher matcher = pattern.matcher(text);
boolean found = matcher.find(); // 返回true,因为"simple"是一个完整的单词// 匹配以特定文本开头的行
String multiLine = "First line\nSecond line\nThird line";
Pattern startPattern = Pattern.compile("^Second", Pattern.MULTILINE);
Matcher startMatcher = startPattern.matcher(multiLine);
boolean matchesStart = startMatcher.find(); // 返回true,因为有一行以"Second"开头

3. 高级正则表达式特性

3.1 零宽断言(Look-around)

零宽断言用于查找特定模式前后的位置,而不消耗任何字符:

  1. 零宽正向先行断言(?=pattern):断言当前位置后面跟着特定模式
  2. 零宽负向先行断言(?!pattern):断言当前位置后面不跟着特定模式
  3. 零宽正向后行断言(?<=pattern)&#x

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/166.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MLLM之Bench:LEGO-Puzzles的简介、安装和使用方法、案例应用之详细攻略

MLLM之Bench&#xff1a;LEGO-Puzzles的简介、安装和使用方法、案例应用之详细攻略 目录 LEGO-Puzzles的简介 1、LEGO-Puzzles的特点 LEGO-Puzzles的安装和使用方法 1、安装 步骤 0&#xff1a;安装 VLMEvalKit 步骤 1&#xff1a;设置 API 密钥&#xff08;可选&#xf…

Java大厂面试突击:从Spring Boot自动配置到Kafka分区策略实战解析

第一轮核心知识 面试官&#xff1a;请解释Spring Boot中自动配置的工作原理并演示如何自定义一个ConfigurationProperties组件&#xff1f; xbhog&#xff1a;自动配置通过EnableAutoConfiguration注解触发&#xff0c;结合当前环境判断&#xff08;如是否检测到MyBatis依赖&…

STM32 定时器TIM

定时器基础知识 定时器就是用来定时的机器&#xff0c;是存在于STM32单片机中的一个外设。STM32总共有8个定时器&#xff0c;分别是2个高级定时器(TIM1、TIM8)&#xff0c;4个通用定时器(TIM2、TIM3、TIM4、TIM5)和2个基本定时器(TIM6、TIM7)&#xff0c;如下图所示: STM32F1…

NEPCON China 2025 | 具身智能时代来临,灵途科技助力人形机器人“感知升级”

4月22日至24日&#xff0c;生产设备暨微电子工业展&#xff08;NEPCON China 2025&#xff09;在上海如期开展。本届展会重磅推出“人形机器人拆解展区”&#xff0c;汇聚35家具身智能产业链领军企业&#xff0c;围绕机械结构、传感器布局、驱动系统与AI算法的落地应用&#xf…

操作系统:计算机世界的基石与演进

一、操作系统的本质与核心功能 操作系统如同计算机系统的"总管家"&#xff0c;在硬件与应用之间架起关键桥梁。从不同视角观察&#xff0c;其核心功能呈现多维价值&#xff1a; 硬件视角的双重使命&#xff1a; 硬件管理者&#xff1a;通过内存管理、进程调度和设…

C++动态分配内存知识点!

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 大家好呀&#xff0c;又是分享干货的时间&#xff0c;今天我们来学习一下动态分配内存。 文章目录 1.动态分配内存的思想 2.动态分配内存的概念 2.1内存分配函数 2.2动态内存的申请和释放 2.3内存碎片问…

2025.4.26总结

今天把马良老师的《职场十二法则》看完后&#xff0c;感触极大&#xff0c;这们课程就是一场职场启蒙课。 虽然看过不少关于职场的书籍&#xff0c;但大多数是关于职场进阶&#xff0c;方法方面的。并没有解答“面对未来二三十年的职场生涯&#xff0c;我该怎么去看待自己的工…

在Spring Boot项目中实现Word转PDF并预览

在Spring Boot项目中实现Word转PDF并进行前端网页预览&#xff0c;你可以使用Apache POI来读取Word文件&#xff0c;iText或Apache PDFBox来生成PDF文件&#xff0c;然后通过Spring Boot控制器提供文件下载或预览链接。以下是一个示例实现步骤和代码&#xff1a; 1. 添加依赖 …

计算机视觉——对比YOLOv12、YOLOv11、和基于Darknet的YOLOv7的微调对比

概述 目标检测领域取得了巨大进步&#xff0c;其中 YOLOv12、YOLOv11 和基于 Darknet 的 YOLOv7 在实时检测方面表现出色。尽管这些模型在通用目标检测数据集上表现卓越&#xff0c;但在 HRSC2016-MS&#xff08;高分辨率舰船数据集&#xff09; 上对 YOLOv12 进行微调时&…

Linux系统编程之内存映射

概述 内存映射是操作系统提供的一种机制&#xff0c;使得文件或设备的内容可以直接映射到进程的虚拟地址空间中。这意味着&#xff0c;我们可以像访问数组一样读写文件内容&#xff0c;而不需要显式地调用I/O函数进行数据传输。内存映射适用于多种应用场景&#xff0c;包括但不…

MyBatis XML 配置完整示例(含所有核心配置项)

MyBatis XML 配置完整示例&#xff08;含所有核心配置项&#xff09; 1. 完整 mybatis-config.xml 配置文件 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""htt…

Java 程序运行和类路径处理

PS D:\java_test> java .\java\Dog 错误: 找不到或无法加载主类 .\java\Dog 原因: java.lang.ClassNotFoundException: /\java\DogJava 程序运行和类路径处理 问题描述 在运行 Java 程序时&#xff0c;可能会遇到 ClassNotFoundException 错误&#xff0c;这是因为 Java 虚…