java 正则表达式的使用

文章目录

简介

什么是正则表达式

正则表达式(Regular Expression,通常简称为regex,regexp或RE),是一种强大的文本处理工具,它是一种用于匹配字符串中字符组合的模式。正则表达式可以用来检查一个字符串是否含有某种子串、将匹配的子串替换或从某个字符串中取出符合某个条件的子串等。

例如,正则表达式 a.b 可以匹配 "acb"、"aeb"、"a1b" 等,. 是一个特殊字符,代表任何字符(除了换行符)。

java 复制代码
String regex = "a.b";
String str = "acb";
boolean matches = str.matches(regex); // returns true

正则表达式在Java中的应用

在Java中,正则表达式主要通过 java.util.regex 包中的两个类 PatternMatcher 来使用。Pattern 类用于编译正则表达式,而 Matcher 类用于执行通过 Pattern 类编译的正则表达式在字符序列上的操作。

正则表达式在Java中的应用广泛,包括但不限于:

  • 验证输入:例如,检查用户输入的电子邮件地址或电话号码是否有效。
  • 搜索文本:例如,搜索日志文件中的特定错误模式。
  • 文本替换:例如,将文本文件中的所有 "colour" 替换为 "color"。
  • 数据提取:例如,从文本文件中提取所有电子邮件地址。
java 复制代码
import java.util.regex.Pattern;
import java.util.regex.Matcher;

Pattern pattern = Pattern.compile("a.b");
Matcher matcher = pattern.matcher("acb");
boolean matches = matcher.matches(); // returns true

Java正则表达式基础

常用的正则表达式符号和规则

正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为"元字符")组成的文本模式。以下是一些常用的元字符和它们的含义:

  • .:匹配任何单个字符(除了换行符)。
  • *:匹配前面的元素零次或多次。
  • +:匹配前面的元素一次或多次。
  • ?:匹配前面的元素零次或一次。
  • ^:匹配输入字符串的开始位置。
  • $:匹配输入字符串的结束位置。
  • {n}:精确匹配n次前面的字符或者表达式。
  • {n,}:匹配n次或更多次前面的字符或者表达式。
  • {n,m}:匹配至少n次,至多m次前面的字符或者表达式。
  • [abc]:匹配方括号内的任何字符,例如 abc
  • [^abc]:匹配除了方括号内字符的任何字符。
  • (pattern):匹配pattern并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到。
  • \d:匹配数字,等价于 [0-9]
  • \D:匹配非数字,等价于 [^0-9]
  • \s:匹配任何空白字符,包括空格、制表符、换页符等等,等价于 [ \f\n\r\t\v]
  • \S:匹配任何非空白字符,等价于 [^ \f\n\r\t\v]
  • \w:匹配字母或数字或下划线或汉字,等价于 [A-Za-z0-9_]
  • \W:匹配任何非单词字符,等价于 [^A-Za-z0-9_]

Java中的正则表达式类:Pattern和Matcher

在Java中,正则表达式的操作主要通过 java.util.regex 包中的两个类 PatternMatcher 来实现。

  • Pattern 类:这是一个正则表达式的编译表示。Pattern 对象是一个多态的表达式,给定的正则表达式必须首先被编译为此类的一个实例。然后,这个模式可以被用来创建 Matcher 对象,以便匹配任意字符序列。

  • Matcher 类:通过解释 Pattern 对象而得到的正则表达式匹配的引擎。所有的匹配操作都通过这个类完成。

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

// 创建 Pattern 对象
Pattern r = Pattern.compile("[a-z]+");

// 现在创建 matcher 对象
Matcher m = r.matcher("hello world");
boolean b = m.matches();

Java正则表达式实例

字符串匹配

在Java中,我们可以使用 String 类的 matches() 方法来测试字符串是否匹配给定的正则表达式。

java 复制代码
String content = "Hello, this is a test.";
 // 返回 true
boolean isMatch = content.matches(".*test.*");

在上述代码中,.*test.* 是一个正则表达式,它表示任何包含 "test" 的字符串。

字符串搜索

我们可以使用 Matcher 类的 find() 方法来搜索字符串中的子串。

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

String content = "I am a student. I am learning Java.";
Pattern pattern = Pattern.compile("am");
Matcher matcher = pattern.matcher(content);

while (matcher.find()) {
    System.out.println("Found at: " + matcher.start() + " - " + matcher.end());
    // Found at: 2 - 4
    // Found at: 18 - 20
}

在上述代码中,我们创建了一个 Pattern 对象,用于编译正则表达式 "am",然后使用 Matcher 对象来查找与该模式匹配的子串。

字符串替换

我们可以使用 String 类的 replaceAll() 方法来替换字符串中匹配给定正则表达式的子串。

java 复制代码
String content = "I am a student. I am learning Java.";
String updatedContent = content.replaceAll("am", "AM");
String updatedBlankSpace = content.replaceAll("\\s+", "_")

// 输出 "I AM a student. I AM learning Java."
System.out.println(updatedContent); 
// 输出 "I_am_a_student._I_am_learning_Java."
System.out.println(updatedBlankSpace); 

字符串分割

我们可以使用 String 类的 split() 方法来根据给定的正则表达式分割字符串。

java 复制代码
String content = "I am   a student. I     am learning           Java.";
String[] words = content.split("\\s+"); // 使用一个或多个空格进行分割

for (String word : words) {
    System.out.println(word);
}

验证电子邮件地址

  • ^:表示字符串的开始。
  • [\\w-]+:匹配一个或多个字母、数字、下划线或连字符。这部分匹配电子邮件地址的本地部分(即@符号之前的部分)。
  • (\\.[\\w-]+)*:匹配一个点号后跟一个或多个字母、数字、下划线或连字符的序列,出现0次或多次。这部分允许本地部分包含点号分隔的子部分。
  • @:匹配一个@符号。
  • [\\w-]+:匹配一个或多个字母、数字、下划线或连字符。这部分匹配域名的第一部分。
  • (\\.[\\w-]+)*:匹配一个点号后跟一个或多个字母、数字、下划线或连字符的序列,出现0次或多次。这部分允许域名包含点号分隔的子域。
  • (\\.[a-zA-Z]{2,}):匹配一个点号后跟两个或更多字母。这部分匹配顶级域名(如 .com.org 等)。
  • $:表示字符串的结束。

所以,这个正则表达式的含义是:一个字符串从开始到结束必须是一个有效的电子邮件地址,格式为 local-part@domain,其中 local-partdomain 可以包含字母、数字、下划线、连字符和点号,顶级域名部分至少包含两个字母。

需要注意的是,这个正则表达式虽然涵盖了大多数常见的电子邮件格式,但并不完全符合所有可能的电子邮件格式。例如,它不支持带有引号的本地部分或包含特殊字符的域名。如果你需要一个更精确的电子邮件正则表达式,可能需要使用更复杂的模式。

java 复制代码
String email = "[email protected]";
Pattern pattern = Pattern.compile("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)*(\\.[a-zA-Z]{2,})$");
Matcher matcher = pattern.matcher(email);
System.out.println(matcher.matches()); // 输出:true 或 false

验证手机号码(假设我们只接受10位数字)

java 复制代码
String phoneNumber = "1234567890";
Pattern pattern = Pattern.compile("^\\d{10}$");
Matcher matcher = pattern.matcher(phoneNumber);
System.out.println(matcher.matches()); // 输出:true 或 false

验证URL

  • ^:表示字符串的开始。
  • (http|https|ftp)://:匹配URL的协议部分,可以是http、https或ftp,后面跟着://
  • [^\\s/$.?#].:匹配URL的主机名部分。具体来说:
    • [^\\s/$.?#]:匹配除空白字符、/$.?#之外的任何字符。
    • .:匹配任何字符,除了换行符。
  • [^\\s]*:匹配除空白字符之外的任何字符,出现0次或多次。
  • $:表示字符串的结束。

所以,这个正则表达式的含义是:一个字符串从开始到结束必须是一个有效的URL,格式为 protocol://hostname/path,其中 protocol 可以是http、https或ftp,hostnamepath 不能包含空白字符、/$.?#

需要注意的是,这个正则表达式并不完全符合URL的规范,例如它不支持包含/$.?#的路径,也不支持包含.的主机名。如果你需要一个更精确的URL正则表达式,可能需要使用更复杂的模式。

java 复制代码
String url = "http://www.example.com";
Pattern pattern = Pattern.compile("^(http|https|ftp)://[^\\s/$.?#].[^\\s]*$");
Matcher matcher = pattern.matcher(url);
System.out.println(matcher.matches()); // 输出:true 或 false

验证IP地址

  • ^:表示字符串的开始。
  • ((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}:匹配前三段数字和它们后面的点号。
    • 25[0-5]:匹配250到255之间的数字。
    • 2[0-4]\\d:匹配200到249之间的数字。
    • [01]?\\d\\d?:匹配0到199之间的数字。具体来说:
      • [01]?:匹配0或1,出现0次或1次。
      • \\d\\d?:匹配一个或两个数字。
    • \\.:匹配一个点号。
    • {3}:表示前面的模式(即 ((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.))重复3次。
  • (25[0-5]|2[0-4]\\d|[01]?\\d\\d?):匹配最后一段数字。
    • 这部分与前面的模式相同,但不包含点号。
  • $:表示字符串的结束。

所以,这个正则表达式的含义是:一个字符串从开始到结束必须是一个有效的IPv4地址,格式为 xxx.xxx.xxx.xxx,其中每个 xxx 是0到255之间的数字。

java 复制代码
String ip = "192.168.1.1";
Pattern pattern = Pattern.compile("^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");
Matcher matcher = pattern.matcher(ip);
System.out.println(matcher.matches()); // 输出:true 或 false

提取字符串中的数字

java 复制代码
String str = "abc123def456";
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
    System.out.println(matcher.group());
    // 输出 123 456
}

验证是否是数字

  • ^:表示字符串的开始。
  • \\d:匹配一个数字字符。\\d 是一个转义序列,表示任何一个数字(0-9)。
  • +:表示前面的模式(即 \\d)出现一次或多次。如果没有这个符号,正则表达式就只会匹配一个数字字符。
  • $:表示字符串的结束。

所以,这个正则表达式的含义是:一个字符串从开始到结束只包含一个或多个数字字符。

java 复制代码
String number = "12345";
Pattern pattern = Pattern.compile("^\\d+$");
Matcher matcher = pattern.matcher(number);
System.out.println(matcher.matches()); // 输出:true 或 false

验证是否是字母

  • ^:表示字符串的开始。
  • [a-zA-Z]:这是一个字符集,匹配任何一个英文字母,无论大小写。a-z 表示所有小写字母,A-Z 表示所有大写字母。在字符集中,- 是一个范围操作符,表示范围从前面的字符到后面的字符。
  • +:表示前面的模式(即 [a-zA-Z])出现一次或多次。如果没有这个符号,正则表达式就只会匹配一个字符。
  • $:表示字符串的结束。
    所以,这个正则表达式的含义是:一个字符串从开始到结束只包含一个或多个英文字母(无论大小写)。
java 复制代码
String letters = "abcABC";
Pattern pattern = Pattern.compile("^[a-zA-Z]+$");
Matcher matcher = pattern.matcher(letters);
System.out.println(matcher.matches()); // 输出:true 或 false

验证是否是日期(格式:yyyy-mm-dd)

这个正则表达式的含义如下:

  • ^:表示字符串的开始。
  • \\d{4}:匹配四个数字。\\d 是一个表示数字字符的转义序列,{4} 表示前面的模式(即数字字符)要出现4次。
  • -:匹配一个连字符(减号)。
  • \\d{2}:匹配两个数字。\\d 是一个表示数字字符的转义序列,{2} 表示前面的模式(即数字字符)要出现2次。
  • -:匹配一个连字符(减号)。
  • \\d{2}:匹配两个数字。\\d 是一个表示数字字符的转义序列,{2} 表示前面的模式(即数字字符)要出现2次。
  • $:表示字符串的结束。
java 复制代码
String date = "2024-06-02";
Pattern pattern = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$");
Matcher matcher = pattern.matcher(date);
System.out.println(matcher.matches()); // 输出:true 或 false

验证密码强度(至少8个字符,包含大写字母,小写字母和数字)

这个正则表达式的含义如下:

  • ^ 表示字符串的开始。
  • (?=.*[0-9]) 确保字符串中至少包含一个数字。
  • (?=.*[a-z]) 确保字符串中至少包含一个小写字母。
  • (?=.*[A-Z]) 确保字符串中至少包含一个大写字母。
  • .{8,}$ 确保字符串的长度至少为8个字符,. 表示任意字符,{8,} 表示至少出现8次,$ 表示字符串的结尾。
java 复制代码
String password = "Password123";
Pattern pattern = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$");
Matcher matcher = pattern.matcher(password);
System.out.println(matcher.matches()); // 输出:true 或 false

Java正则表达式的实现

Java正则表达式主要由 Pattern 类和 Matcher 类实现。下面我们来详细了解一下这两个类的实现。

Pattern类的实现

Pattern 类是Java正则表达式的编译表示,它的实例是不可变的,可以安全地被多个并发线程使用。Pattern 类没有公开的构造函数,要创建一个 Pattern 对象,你必须首先调用其公开的静态 compile 方法,它返回一个 Pattern 对象。这个方法需要一个正则表达式作为它的第一个参数。

java 复制代码
Pattern pattern = Pattern.compile("[a-z]+");

在上述代码中,我们创建了一个 Pattern 对象,它表示一个或多个小写字母的序列。

Pattern 类还提供了一些其他的方法,例如:

  • matcher(CharSequence input):生成一个 Matcher 对象,用于匹配给定的字符序列。
  • split(CharSequence input):根据此模式分割给定的输入序列。
  • pattern():返回此模式的字符串表示形式。

Matcher类的实现

Matcher 类是通过解释 Pattern 对象而得到的正则表达式匹配的引擎。所有的匹配操作都通过这个类完成。

你不能直接创建一个 Matcher 对象,而是需要通过调用 Pattern 对象的 matcher 方法来创建一个 Matcher 对象。

java 复制代码
Pattern pattern = Pattern.compile("[a-z]+");
Matcher matcher = pattern.matcher("hello");

在上述代码中,我们创建了一个 Matcher 对象,用于匹配字符串 "hello"。

Matcher 类提供了一些方法来进行匹配操作,例如:

  • matches():尝试将整个区域与模式进行匹配。
  • find():尝试查找与模式匹配的输入序列的下一个子序列。
  • group():返回由以前匹配操作所匹配的输入子序列。
  • start():返回以前匹配的初始索引。
  • end():返回最后匹配字符之后的索引。

这就是Java正则表达式的实现,主要由 Pattern 类和 Matcher 类完成。

Java正则表达式的性能优化

在Java中,正则表达式是一个强大而灵活的工具,但如果不正确使用,可能会导致性能问题。以下是一些优化Java正则表达式性能的建议:

预编译正则表达式

在Java中,每次调用 String 类的 matches(), split(), replaceAll() 等方法时,都会编译正则表达式,这是一个相对耗时的操作。如果你在循环或频繁调用的方法中使用这些方法,应考虑预编译正则表达式。

预编译正则表达式意味着你创建一个 Pattern 对象,然后重复使用它。这样,正则表达式只需要编译一次,而不是每次使用时都编译。

java 复制代码
Pattern pattern = Pattern.compile("[a-z]+");

for (String input : inputs) {
    Matcher matcher = pattern.matcher(input);
    if (matcher.matches()) {
        // do something
    }
}

在上述代码中,正则表达式只编译了一次,然后在循环中重复使用。

使用非捕获组

在正则表达式中,使用括号 () 可以创建一个组,这意味着正则表达式引擎会记住与括号中的表达式匹配的文本。然而,这个记忆功能有时是不必要的,而且会消耗额外的资源。

如果你不需要记住匹配的文本,可以使用非捕获组 (?:) 代替普通组。非捕获组的语法是在括号内的第一个字符位置使用 ?:

比如,(?:[a-z]+)\d* 匹配一个或多个小写字母后面跟着零个或多个数字,但是不会记住匹配的字母。

这两个优化技巧可以帮助你提高Java正则表达式的性能。然而,你应该注意,正则表达式不总是解决问题的最佳工具。在某些情况下,使用其他的字符串处理方法可能更有效。

相关推荐
腥臭腐朽的日子熠熠生辉几秒前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian2 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之8 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码28 分钟前
Spring Task 定时任务
java·前端·spring
俏布斯40 分钟前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息1 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
绝顶少年2 小时前
Spring Boot 注解:深度解析与应用场景
java·spring boot·后端