Java 正则表达式

Java 正则表达式

Java 的匹配默认为贪婪匹配,尽可能多的去匹配。

匹配规则

规则作用示例示例说明
[]可接收的字符列表[efg]可接收e、f、g中任意一个字符
[^]不接收的字符列表[^efg]除e、f、g之外的任意一个字符
-连字符A-Z任意一个大写字母
.匹配除\n为的任意字符a…b以a开头,b结尾,中间包括2个任意字符的长度为4的字符串
\\d匹配单个数字字符\\d{3}\\d?匹配 3 个或 4 个数字的字符串
\\D匹配单个非数字字符\\D(\\d)*以单个非数字开头, 后接任意个数的数字字符串
\\w匹配单个数字、大小写字母的字符以及下划线
\\W\\w 相反\\W+以至少1个非数字字母开头的字符串
\\s匹配单个空白符
\\S匹配非空白符
(?i)符号之后的内容都不区分大小写了,除非用括号指定范围
^指定起始字符1+[a-z]*表示以至少一个数字开头, 后接任意个数的小写字母组成的字符串
$指定结束字符[a-z]+$表示以至少一个小写字母结尾的字符串
\\b匹配目标字符串的边界。这里的边界指的是字符串之后跟空白字符为边界,或者就是整个字符串的最后
\\B匹配目标字符串的非边界
?单独使用,匹配0或1个出现的前一个表达式;与其他匹配符号一起使用,表示非贪婪匹配
*匹配0个或多个出现的前一个表达式。ba*$以 b 或 ba…结尾的字符串
+匹配1个或多个出现的前一个表达式^a+开头至少有 1 个 a 的字符串
{n}匹配正好n次出现的前一个表达式
{n,}匹配至少n次出现的前一个表达式
{n,m}匹配至少n次且不超过m次出现的前一个表达式
|匹配两个或多个表达式之一

初体验

使用 Java 正则表达式匹配4个连在一起的阿拉伯数字。

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

/**
 * 使用正则表达式匹配4个连在一起的阿拉伯数字。
 */
public class RegExp01 {
    public static void main(String[] args) {
        String content = "1889xcj+dsb&c1198cdj==fhs7863xbc-ndb34ms_ndc3dh%fjc";

        // 目标:匹配4个连在一起的数字
        String regStr = "\\d\\d\\d\\d";

        // 创建模式匹配对象
        Pattern pattern = Pattern.compile(regStr);
        
        // 创建匹配器
        Matcher matcher = pattern.matcher(content);
        
        while (matcher.find()) {
            System.out.println("找到:" + matcher.group(0));
        }
    }
}

分析 matcher.find()

以上面给出的代码分析:

  1. 根据指定的规则,定位满足规则的 子字符串,比如: 1889。

  2. 找到后,将 子字符串 开始的索引记录到 matcher 对象的 groups 属性中(int[] groups)。

    groups[0] = 0, 把该子字符串的 结束索引+1 的值记录到 group[1] = 4,

    同时记录 oldLast 的值为 子字符串结束索引+1, 即 4,下次执行 find() 时, 就从 4 开始匹配。

  3. matcher.group(0)

    根据 groups[0]group[1] 记录的位置, 从 content 开始截取子字符串返回。

    源码

    public String group(int group) {
        if (first < 0)
            throw new IllegalStateException("No match found");
        if (group < 0 || group > groupCount())
            throw new IndexOutOfBoundsException("No group " + group);
        if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
            return null;
        return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
    }
    
  4. 如果再次指向 find 方法, 重复上述步骤(1198)

    groups[0] 变为 13, groups[1] 变为 17,
    oldLast 变为 17, 下次进行 find() 时, 就从17 开始,
    然后根据 groups[0]group[1] 记录的新位置, 从 content 开始截取子字符串返回。

分组

(\d\d)(\d\d) 为匹配规则例:

  • 整体(括号去掉)为第 0组,等效为 \d\d\d\d

  • 第一个括号为第 1 组, 第二个括号为第 2 组···

  • 如果将其替换为 ((\d\d)(\d\d)),那么第 0 组 和第 1组的内容是一致的。

分组实例

/**
 * 分组
 */
public class RegExp02 {
    public static void main(String[] args) {
        String content = "1889xcj+dsb&c1198cdj==fhs7863xbc-ndb34ms_ndc3dh%fjc";

        // 目标:匹配4个连在一起的数字
        String regStr = "(\\d\\d)(\\d\\d)";

        // 创建模式匹配对象
        Pattern pattern = Pattern.compile(regStr);
        
        // 创建匹配器
        Matcher matcher = pattern.matcher(content);
        
        while (matcher.find()) {
            System.out.println("===========================");
            System.out.println("0 组:" + matcher.group(0));
            System.out.println("1 组:" + matcher.group(1));
            System.out.println("2 组:" + matcher.group(2));
            System.out.println("===========================");
        }
    }
}

运行结果:

分析 matcher.find()

  1. 根据指定的规则, 定位满足规则的子字符串(比如:1889);

  2. 找到后, 将 子字符串 开始的索引记录到 matcher 对象的 groups 属性中;

记录0组:groups[0] = 0, 把该子字符串的 结束索引+1 的值记录到 group[1] = 4

记录1组:groups[2] = 0, groups[3] = 2

记录2组:groups[4] = 2, groups[4] = 4

同时记录 oldLast 的值为 子字符串的结束的 索引+1 的值,即 4,下次执行 find() 时, 就从 4 开始匹配

命名分组

语法格式:(?<name>patter)

命名分组又称命名捕获,将匹配的子字符串捕获到一个组名或编号名称中。

  • 用于 name 的字符串不能包含任何的标点符号, 不能以数字开头;
  • 可以使用单引号替代尖括号, 例如:(?'name’patter);
  • 可以使用 name 来获取对应的匹配结果。

\\d\\d(?<one>(?<two>\\d\\d)\\d\\d) 为例

public class RegExp03 {
    public static void main(String[] args) {
        String content = "zhangsanandlisi s128 nn112233zhang";

        regStr = "\\d\\d(?<one>(?<two>\\d\\d)\\d\\d)";
        pattern = Pattern.compile(regStr);
        matcher = pattern.matcher(content);

        while (matcher.find()) {
            // 112233
            System.out.println("0组" + matcher.group());

            // 2233
            System.out.println("one组:" + matcher.group("one"));

            // 22
            System.out.println("two组:" + matcher.group("two"));
        }
    }
}

特别(非捕获)的分组

(?:patter)

匹配 patter,但不捕获该匹配的子表达式, 即它是一个非匹配分组。
不存储供以后使用的匹配。这对于用 or 字符(|)组合模式部件的情况很有用。
例如:industr(?:y|ies) 是比 industry|industries 更好的写法。

实例

public class RegExp04 {
    public static void main(String[] args) {
        String content = "张三是员工、张三是人类、张三是学生、张三是法外狂徒";
        String regStr = "张三是(?:员工|人类|学生)";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            System.out.println("找到:" + matcher.group());

            // 注意:不能使用以下代码
            // matcher.group(1);
        }
    }
}

执行结果:

(?=patter)

它是一个非捕获匹配。

例如:‘Windows(?=95|98|NT|2000)’ 匹配 ‘Windows 2000’ 中的 ‘Windows’, 但是不匹配 ‘Windows 3.1’ 中的 ‘Windows’。

实例

public class RegExp05 {
    public static void main(String[] args) {
        String content = "张三是员工、张三是人类、张三是学生、张三是法外狂徒";
        String regStr = "张三是(?=员工|人类|学生)";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            System.out.println("找到:" + matcher.group());
        }
    }
}

执行结果

(?!patter)

它是一个非捕获匹配,该表达式不存储匹配 patter 的字符串的起始点的搜索字符串。
例如:‘Windows(?!95|98|NT|2000)’ 匹配 ‘Windows 3.1’ 中的 ‘Windows’,
但是不匹配 ‘Windows 2000’ 中的 Windows

实例

public class RegExp06 {
    public static void main(String[] args) {
        String content = "张三是员工、张三是人类、张三是学生、张三是法外狂徒";
        String regStr = "张三是(?!员工|人类|学生)";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            System.out.println("找到:" + matcher.group());
        }
    }
}

执行结果:

正则表达式转义符

Java 正则表达式的转移符号为 \\

在匹配如下符号是需要用到转移符:.*+$/\?[]{}<>

Pattern 和 Matcher

Pattern

Pattern 是一个正则表达式对象, 不能 new。

通过调用公共静态方法 compile(pattern),返回 Pattern 对象。该方法接收的一个正则表达式规则字符串作为它的第一个参数。

matches() 是这个类中常用的一个方法,用于整体匹配,可以用于验证输入的字符串是否符合要求。这个方法的底层实际上是调用了 Matcher 类中的 matches() 方法。

实例

public class RegExp07 {
    public static void main(String[] args) {
        String content = "hello abc hello";
        String regStr = "hello";  // 整体匹配=false
        boolean matches = Pattern.matches(regStr, content);
        System.out.println("整体匹配=" + matches);
        
        regStr = "hello.*";  // 整体匹配=true
        matches = Pattern.matches(regStr, content);
    }
}

Matcher

Marcher 是对输入字符串进行解释和匹配的引擎。
Pattern 类一样,Matcher 不能 new。
创建方式是调用 Pattern 对象的 matcher 方法来返回一个 Matcher 对象。

常用的方法有:

  • start():返回匹配的 初始索引
  • ``end()`:返回匹配的 结束结束索引+1
  • replaceAll():字符串的替换, 不改变原字符串

实例

public class RegExp08 {
    public static void main(String[] args) {
        String content = "hello edu jack tom hello smith hello";
        String regStr = "hello";

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            System.out.println("==============");
            System.out.println(matcher.start());
            System.out.println(matcher.end());
            System.out.println(content.substring(matcher.start(), matcher.end()));
        }

        // 替换。不会改变原来的字符串。
        content = "zhangsan将拼音换为对应汉字";
        regStr = "zhangsan";
        pattern = Pattern.compile(regStr);
        matcher = pattern.matcher(content);
        String newContent = matcher.replaceAll("张三");
        System.out.println(newContent);  // 张三将拼音换为对应汉字

    }
}

运行结果

反向引用

圆括号的内容被捕获以后, 可以在这个括号后被使用, 从而写出一个比较实用的匹配模式。这个我们称为反向引用, 这种引用既可以是在正则表达式内部, 也可以是正则表达式外部。

  • 内部反向引用:\\ 分组号

    • (\\d)\\1:匹配的数字格式 xx,比如11, 22
    • (\\d)(\\d)\\1\\2:匹配的数字格式 xyxy,比如12120101
  • 外部反向引用:$ 分组号

实例

找出格式为 xyyx 的数字字符串,比如 "1221"

public class RegExp09 {
    public static void main(String[] args) {
        String content = "1221 1222 1111 3478 5775 8118 8273";

        String regStr = "(\\d)(\\d)\\2\\1";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            System.out.println("找到:" + matcher.group());
        }
    }
}

结巴去重

public class RegExp10 {
    public static void main(String[] args) {
        // 修改成: 我要学编程Java!
        String content = "我...我要...学学学学...编程Java!";
        
        // 去除所有的 . 号
        String regStr = "[.]";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        content = matcher.replaceAll("");
        System.out.println(content);  // 我我要学学学学编程Java!
        
        // 去重:我我要学学学学编程Java!
        regStr = "(.)\\1+";
        pattern = Pattern.compile(regStr);
        matcher = pattern.matcher(content);
        
        while (matcher.find()) {
            // 以下方法中 $1 是外部反向引用
            content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
        }

        System.out.println(content);
    }
}


  1. 0-9 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值