java 正则表达式的使用

简介

什么是正则表达式

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

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

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”。
  • 数据提取:例如,从文本文件中提取所有电子邮件地址。
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 对象而得到的正则表达式匹配的引擎。所有的匹配操作都通过这个类完成。

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() 方法来测试字符串是否匹配给定的正则表达式。

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

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

字符串搜索

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

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() 方法来替换字符串中匹配给定正则表达式的子串。

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() 方法来根据给定的正则表达式分割字符串。

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 可以包含字母、数字、下划线、连字符和点号,顶级域名部分至少包含两个字母。
需要注意的是,这个正则表达式虽然涵盖了大多数常见的电子邮件格式,但并不完全符合所有可能的电子邮件格式。例如,它不支持带有引号的本地部分或包含特殊字符的域名。如果你需要一个更精确的电子邮件正则表达式,可能需要使用更复杂的模式。

String email = "user@example.com";
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位数字)

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正则表达式,可能需要使用更复杂的模式。

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之间的数字。

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

提取字符串中的数字

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)出现一次或多次。如果没有这个符号,正则表达式就只会匹配一个数字字符。
  • $:表示字符串的结束。

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

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])出现一次或多次。如果没有这个符号,正则表达式就只会匹配一个字符。
  • $:表示字符串的结束。
    所以,这个正则表达式的含义是:一个字符串从开始到结束只包含一个或多个英文字母(无论大小写)。
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次。
  • $:表示字符串的结束。
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次,$ 表示字符串的结尾。
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 对象。这个方法需要一个正则表达式作为它的第一个参数。

Pattern pattern = Pattern.compile("[a-z]+");

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

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

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

Matcher类的实现

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

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

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 对象,然后重复使用它。这样,正则表达式只需要编译一次,而不是每次使用时都编译。

Pattern pattern = Pattern.compile("[a-z]+");

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

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

使用非捕获组

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

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

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

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

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值