云计算百科
云计算领域专业知识百科平台

一篇带你玩转:正则表达式(Java)

一、正则表达式基础

1.1 基本概念

(1)定义:正则表达式是一种用于描述字符串模式的形式化表示法,通过特定字符和它们的组合来创建 “规则字符串”,用于匹配、查找和替换文本。

(2)用途:常用于文本处理任务,如验证用户输入格式(邮箱、电话号码等)、从文本中提取特定信息、替换文本中的某些部分等等。说白了,就是用于处理复杂的字符串。

1.2常用符号与元字符(方便查找

符号

含义

示例及解释

普通字符

匹配自身

如a匹配字符a,5匹配字符5

.

匹配除换行符外的任意单个字符

a.c可匹配abc、a1c等,但不匹配a\\nc

^

匹配字符串的开头

^abc可匹配以abc开头的字符串,如abcdef,但不匹配dabc

$

匹配字符串的结尾

abc$可匹配以abc结尾的字符串,如defabc,但不匹配abcdef

*

匹配前面的字符或子表达式零次或多次

a*可匹配空字符串、a、aa、aaa等;

(ab)*可匹配空字符串、ab、abab等

+

匹配前面的字符或子表达式一次或多次

a+可匹配a、aa、aaa等;

(ab)+可匹配ab、abab等

?

匹配前面的字符或子表达式零次或一次

a?可匹配空字符串或a;

(ab)?可匹配空字符串或ab

[]

字符集合,匹配其中任意一个字符

[abc] 可匹配a或b或c;

[0 – 9]可匹配任意一位数字

[^]

否定字符集合,

匹配不在其中的任意一个字符

[^abc]可匹配除a、b、c之外的任意字符

()

分组,改变优先级或用于反向引用

(ab)+表示ab整体重复多次;

(a(bc))中,\\1代表整个a(bc),\\2代表bc

\\

转义字符,使特殊字符匹配自身

\\.匹配字符.;

\\*匹配字符*

\\d

匹配任意一个数字,等价于[0 – 9]

\\d可匹配0到9中的任意一个数字,

如a\\dc可匹配a1c、a2c等

\\D

匹配任意一个非数字字符,

等价于[^0 – 9]

\\D可匹配除数字外的字符,如\\Da可匹配a、b、@等与数字不同的字符

\\s

匹配任意一个空白字符,包括空格、制表符、换行符等,等价于[ \\f\\n\\r\\t\\v]

如a\\sb可匹配a b(中间有空格)、

a\\n b(a和b中间换行)等

\\S

匹配任意一个非空白字符,

等价于[^ \\f\\n\\r\\t\\v]

\\S可匹配除空白字符外的字符,如\\Sa可匹配1a、ba等,只要不是空白字符开头即可

\\w

匹配任意一个单词字符,包括字母、数字和下划线,等价于[a-zA – Z0 – 9_]

\\w可匹配a到z、A到Z、0到9以及_,如\\w+可匹配abc、123、abc123_等单词字符组合

\\W

匹配任意一个非单词字符,

等价于[^a-zA – Z0 – 9_]

\\W可匹配除单词字符外的字符,

如\\W+可匹配@、#、(空格)等

1.3 量词

1*(零次或多次):匹配前面的字符或子表达式零次或多次。例如a*,可以匹配空字符串,也可以匹配一个或多个连续的a,如a、aa、aaa等。

2+(一次或多次):匹配前面的字符或子表达式一次或多次。例如a+,至少匹配一个a,可以是a、aa、aaa等,但不能匹配空字符串。

3?(零次或一次):匹配前面的字符或子表达式零次或一次。例如a?,可以匹配空字符串,也可以匹配一个a。

4{n}(恰好 n 次):匹配前面的字符或子表达式恰好 n 次。例如a{3},只能匹配连续的三个a,即aaa。

5{n,}(至少 n 次):匹配前面的字符或子表达式至少 n 次。例如a{2,},可以匹配aa、aaa、aaaa等两个或更多个连续的a。

6{n,m}(n 到 m 次):匹配前面的字符或子表达式 n 到 m 次(n 和 m 为非负整数,且 n <= m)。例如a{1,3},可以匹配a、aa、aaa。

1.4 字符集

(1)普通字符集[]:

在方括号内列出的字符,表示匹配其中任意一个字符。例如[abc],可以匹配a、b或c。。

(2)范围字符集[a – z]、[0 – 9]等等:

通过连字符表示字符范围。[a – z]匹配任意一个小写字母,[0 – 9]匹配任意一个数字。

(3)否定字符集[^]:

在方括号内开头使用^,表示匹配不在括号内的任意一个字符。例如[^abc],可以匹配除了a、b、c之外的任意字符。

二、正则表达式的匹配模式

2.1 贪婪匹配与非贪婪匹配

(1)贪婪匹配:

正则表达式默认采用贪婪匹配模式,即尽可能多地匹配字符。例如,对于字符串"aaaab",正则表达式a+会匹配"aaaa",因为它会一直匹配到不能再匹配为止。

(2)非贪婪匹配:

在量词后面加上?表示非贪婪匹配,即尽可能少地匹配字符。对于上述字符串"aaaab",正则表达式a+?会匹配"a",然后继续往后匹配下一个"a"。
 

例题:对于字符串"aabbb",分别用b+和b+?去匹配,b+会匹配"bbb";通过Matcher的find()方法循环查找时,b+?会依次匹配"b"、"b"、"b"。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
public static void main(String[] args) {
String str = "aabbb";
// 贪婪匹配
Matcher greedyMatcher = Pattern.compile("b+").matcher(str);
if (greedyMatcher.find()) {
System.out.println("贪婪匹配结果:" + greedyMatcher.group()); // 输出 "bbb"
}
// 非贪婪匹配
Matcher lazyMatcher = Pattern.compile("b+?").matcher(str);
System.out.print("非贪婪匹配结果:");
while (lazyMatcher.find()) {
System.out.print(lazyMatcher.group() + " "); // 输出 "b b b "
}
}
}

2.2 全局匹配与局部匹配

(1)全局匹配:在 Java 中,通过Matcher的find()方法循环调用实现全局匹配,即遍历字符串中所有符合模式的部分。

(2)局部匹配:默认情况下,使用Matcher的find()方法首次调用仅返回第一个匹配的结果,即局部匹配。
例题:对于字符串"123abc456abc",分别实现局部匹配和全局匹配:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
public static void main(String[] args) {
String str = "123abc456abc";
Pattern pattern = Pattern.compile("abc");
Matcher matcher = pattern.matcher(str);
// 局部匹配(获取第一个结果)
if (matcher.find()) {
System.out.println("局部匹配结果:" + matcher.group()); // 输出 "abc"
}
// 全局匹配(获取所有结果)
System.out.print("全局匹配结果:");
matcher.reset(); // 重置匹配器,重新开始匹配
while (matcher.find()) {
System.out.print(matcher.group() + " "); // 输出 "abc abc "
}
}
}

2.3 区分大小写与不区分大小写匹配

(1)区分大小写匹配:

默认情况下,正则表达式是区分大小写的。例如[A-Z]只能匹配大写字母,[a-z]只能匹配小写字母。对于字符串"Abc",正则表达式[a-z]{3}不能匹配,因为其中包含大写字母A。

(2)不区分大小写匹配:

在 Java 中,通过Pattern.CASE_INSENSITIVE标志实现不区分大小写匹配。

三、正则表达式的分组与捕获

3.1 分组的概念与用途

(1)分组定义:

使用圆括号()将多个字符或子表达式组合在一起,形成一个逻辑单元。例如(ab)+,表示ab这个组合可以重复一次或多次。

(2)用途:

改变优先级,使括号内的部分作为一个整体进行操作;还可用于后续的反向引用。例如(a(bc)),\\1代表整个a(bc),\\2代表bc。例如,对于字符串"abcabc",用(abc)+可以匹配整个字符串,因为abc这个组合重复了两次。

3.2 捕获组与反向引用

(1)捕获组:

每个分组就是一个捕获组,从左到右,从 1 开始为每个捕获组编号。例如在(a(bc))中,有两个捕获组,(a(bc))是第 1 个捕获组,(bc)是第 2 个捕获组。

(2)反向引用:

使用\\n(n 为捕获组编号)来引用之前捕获组匹配到的内容。例如,正则表达式(ab)\\1,可以匹配abab,因为\\1引用了第一个捕获组(ab)匹配到的内容。例题:判断字符串"xyxy"能否被(xy)\\1匹配,结果是可以匹配;判断"xyyx"能否被匹配,结果是不能匹配。

3.3 非捕获组

(1)非捕获组定义:

使用(?:)表示非捕获组,它只起到分组的作用,不会捕获匹配的内容,也不会分配组号。例如(?:ab)+,与(ab)+类似,但(?:ab)+不会产生捕获组。

(2)用途:

当只需要分组改变优先级,而不需要捕获内容时使用非捕获组,可以提高效率,减少资源占用。例如,对于正则表达式(?:a|b)c,它匹配ac或bc,但不会捕获a或b。例题:在字符串"ac"、"bc"、"abc"中,(?:a|b)c可以匹配"ac"和"bc",而(a|b)c虽然也能匹配"ac"和"bc",但会产生捕获组,在一些场景下若不需要捕获组,使用(?:a|b)c更合适。

四、正则表达式在Java中的应用

4.1主要类与方法:

(1)Pattern类:表示编译后的正则表达式模板,通过Pattern.compile(String regex)方法编译正则表达式,得到Pattern对象。

(2)Matcher类:用于执行匹配操作,通过Pattern.matcher(CharSequence input)方法创建Matcher对象,关联输入字符串。

4.2Matcher类的常用方法:

  • boolean find():查找输入字符串中是否有匹配的子串,可多次调用,每次找下一个匹配。
  • boolean matches():判断整个输入字符串是否完全匹配正则规则。
  • String group():返回当前匹配到的整个子串。
  • String group(int n):返回第 n 个捕获组匹配到的内容,n = 0 表示整个匹配。
  • int start():返回当前匹配子串的起始下标。例如int startIndex = matcher.start();。
  • int end():返回当前匹配子串的结束下标(exclusive,即实际结束位置 + 1)。
  • String replaceAll(String replacement):将所有匹配的子串替换为指定字符串。
  • String replaceFirst(String replacement):只替换第一个匹配的子串。

五、正则表达式习题(持续更新)

1.数学表达式解析器

题目:

给出如下表达式:x#y = 2*x+3*y+4             x$y = 3*x+y+2

公式中,$的优先级高于#,相同的运算符,按从左到右的顺序计算。给定表达式请计算出结果

正则表达式使用思路:
  • 目的:解析数学表达式,先处理$运算,再处理#运算
  • 核心思想:使用正则表达式匹配特定模式,提取数字进行计算,然后替换原字符串

    // 导入正则表达式包
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    // 编译正则表达式模式
    Pattern pattern = Pattern.compile("(\\\\d+)\\\\$(\\\\d+)");

    // 创建匹配器并循环查找
    while (true) {
    Matcher matcher = pattern.matcher(str);
    if (!matcher.find()) break;

    // 提取匹配的内容
    String subStr = matcher.group(0); // 完整匹配:"4$5"
    long x = Long.parseLong(matcher.group(1)); // 第一个数字:4
    long y = Long.parseLong(matcher.group(2)); // 第二个数字:5

    // 替换计算结果
    str = str.replaceFirst(subStr.replace("$", "\\\\$"), 3 * x + y + 2 + "");
    }

2.字符类正则表达式的处理

题目:

给定一段“密文”字符串 s,其中字符都是经过“密码”加密的,现需要将“密文”解密并输出。

映射的规则('a' ~ 'i')分别用('1' ~ '9')表示;('j' ~ 'z')分别用("10*" ~ "26*")表示。

正则表达式使用思路:
  • 目的:解密密码,按空格和星号分割输入字符串
  • 核心思想:使用字符类正则表达式匹配多种分隔符

    // 使用字符类正则表达式分割
    String[] arrs = str.split("[ \\\\*]+");

3.提取字符串中的最长合法简单数学表达式

题目:

提取字符串中的长度最长合法简单数学表达式,并计算表达式的值。如果没有,则返回 0 。

简单数学表达式只能包含以下内容:

  • 0-9数字,符号+-*

说明:

  • 如果有多个长度一样的,请返回第一个表达式的结果
  • 操作符不能连续出现,如 +–+1 是不合法的
  • 正则表达式使用思路:
    • 目的: 从字符串中提取最长的有效数学表达式并计算结果
    • 核心思想: 使用捕获组正则表达式匹配数学运算表达式

    // 使用捕获组正则表达式匹配数学运算
    String regex01 = "([0-9]+)\\\\+([0-9]+)"; // 加法表达式
    String regex02 = "([0-9]+)-([0-9]+)"; // 减法表达式
    String regex03 = "([0-9]+)\\\\*([0-9]+)"; // 乘法表达式

    // 下面仅对加法表达式进行演示
    Pattern pattern01 = Pattern.compile(regex01);
    Matcher matcher01 = pattern01.matcher(text);
    if (matcher01.find()) {
    String fullMatch01 = matcher01.group(0); // 整个式子
    int preExpression01 = Integer.parseInt(matcher01.group(1)); // 第一部分
    int rearExpression01 = Integer.parseInt(matcher01.group(2)); // 第二部分
    }

    4.复杂的字符串组合的处理

    题目:

    对数字,字符,数字串,字符串,以及数字与字符串组合进行倒序排列。

  • 字符范围:由 a 到 z, A 到 Z,
  • 数字范围:由 0 到 9
  • 符号的定义:

    • “-”作为连接符使用时作为字符串的一部分,例如“11-22”作为一个整体字符串呈现;
    • 连续出现 2 个 “-” 及以上时视为字符串间隔符,如“a-b”中的”–“视为间隔符,是 2 个独立整体字符串”a”和”b”;
    • 除了 1,2 里面定义的字符以外其他的所有字符,全都作为间隔符处理,倒序后间隔符作为空格处理;
    • 要求倒排后的单词间隔符以一个空格表示;如果有多个间隔符时,只允许出现一个空格
    正则表达式使用思路:
    • 目的: 识别合法单词和分隔符,倒序输出单词
    • 核心思想: 使用分组正则表达式区分合法单词和分隔符

      // 使用分组正则表达式区分单词和分隔符
      // 1. 定义正则:匹配“合法单词”(含单个'-'的组合或者不含)和“分隔符”(连续–+或非法字符)
      // 分组1: 合法单词(字母、数字、单个'-'组合)
      // 分组2: 分隔符(连续–+或非法字符)
      Pattern pattern = Pattern.compile("([a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?)|([^a-zA-Z0-9-]|–+)");

      /*
      * 对上述正则表达式的解释:
      * 整体结构为 (分组1)|(分组2);为或结构
      * (1)分组1:([a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?)的说明:
      * 非捕获分组(?: 表示不捕获该分组),整体是可选的(最多出现1次):
      * -:匹配单个短横线;
      * [a-zA-Z0-9]+:短横线后必须接至少1个字母/数字(避免-出现在结尾);
      * ?:量词,表示整个分组“出现0次或1次”(即最多只能有一个-)。
      * (2)([^a-zA-Z0-9-]|–+)
      * [^…] 表示 “非字符集”,即匹配除了字母、数字、单个-之外的所有字符
      * –+ 整体匹配 “连续2个及以上的短横线”(如 –、— 等)
      */

      Matcher matcher = pattern.matcher(str);
      while (matcher.find()) {
      String word = matcher.group(1); // 合法单词
      String separator = matcher.group(2); // 分隔符
      }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 一篇带你玩转:正则表达式(Java)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!