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对象:
import java.util.regex.Pattern ; public class RegexExample { public static void main(String[] args) { // 编译正则表达式 Patternpattern= Pattern.compile("a.*c" ) ; } } 3.2 创建Matcher对象并执行匹配
编译后的Pattern对象用于创建Matcher对象,之后凭借Matcher对象执行匹配操作:
import java.util.regex.Matcher ; import java.util.regex.Pattern ; public class RegexExample { public static void main(String[] args) { // 编译正则表达式 Patternpattern= Pattern.compile("a.*c" ) ; // 创建Matcher对象 Matchermatcher=pattern.matcher("abc" ) ; // 执行匹配操作 booleanisMatch=matcher.matches( ) ; System.out.println("是否匹配: " +isMatch) ; // 输出: true } } 3.3 常用的Matcher手段
matches():尝试将整个输入序列与模式匹配。find():在输入序列中查找下一个匹配的子序列。group():返回当前匹配的子序列。start():返回当前匹配的子序列的起始索引。end():返回当前匹配的子序列的结束索引加1。
示例代码:
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!" ; Patternpattern= Pattern.compile("Hello" ) ; Matchermatcher=pattern.matcher(input) ; // 查找所有匹配项 while (matcher.find( ) ) { System.out.println("匹配到: " +matcher.group( ) + ", 起始位置: " +matcher.start( ) + ", 结束位置: " +matcher.end( ) ) ; } } } 输出结果:
匹配到: Hello , 起始位置: 0 , 结束位置: 5 匹配到: Hello , 起始位置: 14 , 结束位置: 19 四、正则表达式高级应用
4.1 分组与捕获
使用圆括号()行将正则表达式中的部分内容分组,每个分组行被单独捕获和引用。分组编号从1开始,0表示整个匹配结果。
示例:匹配并提取邮箱地址中的用户名和域名
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,})" ; Patternpattern= Pattern.compile(regex) ; Matchermatcher=pattern.matcher(email) ; if (matcher.matches( ) ) { System.out.println("完整匹配: " +matcher.group(0 ) ) ; // 整个匹配结果 System.out.println("用户名: " +matcher.group(1 ) ) ; // 第一组 System.out.println("域名: " +matcher.group(2 ) ) ; // 第二组 } } } 输出结果:
完整匹配: test.user@example.com 用户名: test.user 域名:example.com 4.2 反向引用
在正则表达式中,可以运用\n(n为数字)引用前面已经捕获的分组。例如,匹配重复的单词:
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" ; Patternpattern= Pattern.compile(regex) ; Matchermatcher=pattern.matcher(text) ; while (matcher.find( ) ) { System.out.println("重复的单词: " +matcher.group( ) ) ; } } } 输出结果:
重复的单词:hello hello 重复的单词:world world 4.3 贪婪匹配与非贪婪匹配
- 贪婪匹配:默认情况下,正则表达式的量词(如
*、+、{n,m})是贪婪的,会尽可能多地匹配字符。 - 非贪婪匹配:在量词后面加上
?,可能将贪婪匹配转换为非贪婪匹配,尽可能少地匹配字符。
示例:
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>" ; // 贪婪匹配 StringgreedyRegex= "<.*>" ; PatterngreedyPattern= Pattern.compile(greedyRegex) ; MatchergreedyMatcher=greedyPattern.matcher(text) ; if (greedyMatcher.find( ) ) { System.out.println("贪婪匹配: " +greedyMatcher.group( ) ) ; } // 非贪婪匹配 StringnonGreedyRegex= "<.*?>" ; PatternnonGreedyPattern= Pattern.compile(nonGreedyRegex) ; MatchernonGreedyMatcher=nonGreedyPattern.matcher(text) ; while (nonGreedyMatcher.find( ) ) { System.out.println("非贪婪匹配: " +nonGreedyMatcher.group( ) ) ; } } } 输出结果:
贪婪匹配: <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"结尾的单词
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)" ; Patternpattern= Pattern.compile(regex) ; Matchermatcher=pattern.matcher(text) ; while (matcher.find( ) ) { System.out.println("匹配到: " +matcher.group( ) ) ; } } } 输出结果:
匹配到: run 匹配到: jump 匹配到: swim 五、正则表达式在实际制作中的应用
5.1 表单验证
正则表达式常用于表单验证,确保用户输入的数据符合预期格式。
邮箱验证
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) ; } 手机号验证
public static boolean isValidPhone(String phone) { String regex = "^1[3-9]\\d{9}$" ; return Pattern.matches(regex, phone) ; } 身份证号码验证
public static boolean isValidIdCard(StringidCard) { 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标签
public static String removeHtmlTags(String html) { String regex = "<[^>]+>" ; return html.replaceAll(regex, "" ) ; } 敏感词过滤
public static String filterSensitiveWords(String text) { String[]sensitiveWords= { "敏感词1" , "敏感词2" , "敏感词3" } ; String regex = String.join("|" ,sensitiveWords) ; return text.replaceAll(regex, "***" ) ; } 5.3 文本分割
使用正则表达式允许按复杂规则分割文本。
按逗号或空格分割
public static String[] splitText(String text) { String regex = "[,\\s]+" ; return text.split(regex) ; } 按数字分割
public static String[] splitByNumbers(String text) { String regex = "\\d+" ; return text.split(regex) ; } 六、正则表达式性能优化
- 编译一次,多次使用:避免在循环中重复编译相同的正则表达式,应将编译后的
Pattern对象缓存并复用。 - 简化正则表达式:复杂的正则表达式会降低匹配效率,尽量采用方便、明确的表达式。
- 避免过度使用回溯:贪婪匹配和反向引用可能导致大量回溯,影响性能。
- 优先使用String类的方法:对于便捷的字符串操控,如
startsWith()、endsWith()、indexOf()等,应优先使用String类的办法,比正则表达式效率更高。
七、常见问题与注意事项
- 转义字符挑战:在Java字符串中使用正则表达式时,需注意转义字符。例如,匹配点号
.需要写成\\.,匹配反斜杠\需要写成\\\\。 - 性能问题:复杂的正则表达式可能导致性能问题,特定是在处理大量信息时。
- 边界问题:使用
^和$时要注意是否需要匹配整个字符串,还是只需要匹配部分内容。 - Unicode支持:Java默认支持Unicode字符,但在处理非ASCII字符时需要特别注意。
总结
正则表达式是Java中强大的文本处理工具,掌握正则表达式的根本语法和Java中的运用方法,对于提高字符串处理效率和开发质量至关重要。本文从基础概念入手,详细介绍了正则表达式的语法、Java中的API运用、高级应用场景以及性能优化等方面的内容,希望你在今后熟练启用尽量掌握。
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ
浙公网安备 33010602011771号