正则表达式

目录
一、步骤
二、底层源码
三、正则表达式语法
四、正则表达式的三个常用类
五、String中使用正则表达式演示
六、总结


一、步骤

在这里插入图片描述

📌代码演示

public class RegTheory {
    public static void main(String[] args) {
        String regStr = "(\\d\\d)(\\d\\d)";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher("1999sAsdadasd123adasc1326asdA");
        while (matcher.find()){
            System.out.println(matcher.group(0));
            System.out.println(matcher.group(1));
            System.out.println(matcher.group(2));
        }
    }
}

二、底层源码

关于matcher.groups()中的参数为0,1…的讨论❓

// search(nextSearchIndex)
boolean search(int from) {
    this.hitEnd = false;
    this.requireEnd = false;
    from        = from < 0 ? 0 : from;
    this.first  = from;
    this.oldLast = oldLast < 0 ? from : oldLast;
    for (int i = 0; i < groups.length; i++)
        groups[i] = -1;
    acceptMode = NOANCHOR;
    boolean result = parentPattern.root.match(this, from, text);
    if (!result)
        this.first = -1;
    this.oldLast = this.last;
    return result;
}

1. 无分组

  • matcher.find()search(nextSearchIndex)。真正找到索引的是search()
  • 根据指定的规则,定位满足规则的子字符串(比如1998)。
  • 找到后,将子字符串的开始的索引记录到 matcher对象的属性int[] groupsgroups[0] = 0,把该子字符串的结束的索引+1的值记录到 groups[1] =4。
  • 同时记录oldLast的值为子字符串的结束的索引+1的值即4,即下次执行find时,就从4开始匹配。

2. 有分组

  • 分组的概念:一个括号就是一个分组。
  • groups数组是存储匹配到结果的地方。
  • 和无分组一样,groups[0]和groups[1]位置存储的是匹配到的开始的索引和结束索引+1;接下来groups[2]groups[3]存储的是匹配到的第一组的开始的索引和结束索引+1;groups[4]groups[5]存储的是匹配到的第二组的开始的索引和结束索引+1;如果有更多分组则以此类推。
// match.group()方法
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();
}
  • 我们在取出匹配到的数据的时候matcher.group()中的参数是0的时候,我们可以根据getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();看出他调用的是getSubSequence(groups[0],groups[1]),而groups[0]和groups[1]位置存储的是匹配到的开始的索引和结束索引+1,所以他刚好能返回匹配到的结果。
  • 如果参数是1,则是getSubSequence(groups[2],groups[3]),也会取到第一组匹配到的数据。
  • 他是group * 2的原因在于它是需要两个位置来记录开始索引和结束索引+1这两个索引。

💡注意:
 需要区分groupsgroupgroups是matcher的一个属性,它是一个数组用来存储索引;而group是group方法的参数。

三、正则表达式语法

1. 元字符

1.1 转义符

java的正则表达式中,两个\\代表其他语法中的一个\。当我们在使用正则表达式去检索某些特殊字符的时候,需要用到转移符号,否则检索不到结果,甚至会报错。

 需要用到转移符号的字符有以下:. * + ( ) $ / \ ? [ ] ^ { }

1.2 字符匹配符

符号符号示例解释
[]可接收的字符列表[efgh]e、f、g、h中的任意1个字符
[^]不接收的字符列表[^abc]除a、b、c之外的任意1个字符, 包括数字和特殊符号
-连字符A-Z任意单个大写字母
.匹配除\n以外的任何字节a…b以a开头,b结尾,中间包括2个任意字符的长度为4的字符串
\\d匹配单个数字字符,相当于[0-9]\\d{3}(\\d)?包含3个或4个数字的字符串
\\D匹配单个非数字字符,相当于[^0-9]\\D(\\d)*以单个非数字字符的开头,后接任意个数字字符串
\\w匹配单个数字、大小写字母字符,相当于[0-9a-zA-Z]\\d{3}\\w{4}以3个数字字符开头的长度为7的数字字母字符串
\\W匹配单个非数字,大小写字母字符,相当于[^0-9a-zA-Z]\\W+\\d{2}以至少1个非数字字母字符开头,2个数字字符结尾的字符串
\\s匹配任何空白字符(空格,制表符等)
\\S匹配任何非空白字符,和\\s刚好相反

💡说明:
 java正则表达式默认是区分字母的大小写。
如何实现不区分大小写的方式❓

  • 方式一:在需要不区分大小写的表达式前面写上(?i)。但需要注意的是,如果是表达式中间的某个字符不区分大小写的时候需要将它们括起来,例如:a((?i)b)c
  • 方式二:Pattern pat = Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);

如果在中括号里面写上这些符号,则代表这些符号本身。

2. 限定符

符号含义示例说明匹配输入
*指定字符重复0次或n次(无要求)零到多(abc)*仅包含任意个abc的字符串,等效于\w*abc、 abcabcabc
+指定字符重复1次或n次(至少 一次)1到多m+(abc)*以至少1个m开头,后接任意个abc的字 符串m、mabc、 mabcabc
?指定字符重复0次或1次(最多 一次)0到1m+abc?以至少1个m开头,后接ab或abc的字符 串mab、mabc、mmmab. mmabc
{}只能输入n个字符[abcd]{3}由abcd中字母组成的任意长度为3的字 符串abc、dbc、adc
{n,}指定至少n个匹配[abcd]{3,}由abcd中字母组成的任意长度不小于3的字符串aab、dbc、aaabdc
{n,m}指定至少n个但不多于m个匹配[abcd]{3,5}由abcd中字母组成的任意长度不小于3,不大于5的字符串abc、abcd、aaaaa、bcdab

💡说明:

  1. java默认为贪婪匹配。即:在限定出现次数里,优先匹配出现次数最多的一组。例如:用a{3,5}来匹配aaaaaacx,返回的式aaaaa。
  2. ?组合的时候,它只作用于离他最近的那一个字符。例如:m+abc?中,?只作用于字符c。

如果想要使用非贪婪匹配,则在限定符后面加上?即可。

2.1 选择匹配符

符号符号示例解释
|匹配“|”之前或之后的表达式ab|cdab或者cd

2.2 分组组合和反向引用符

📌相关概念了解

  1. 分组

 我们可以用圆括号组成一个比较复杂的四配模式,那么一个圆括号的部分我们可以看作是一个子表达式或一个分组

  1. 捕获分组

 把正则表达式中子表达式分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式

常用分组构造形式说明
(pattern)非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号。(在分析底层源码的时候说过)
(?pattern)命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包括任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如(?`name`)

📌演示代码:

public class RegTheory {
    public static void main(String[] args) {
        String content = "123-abc";
        String regStr = "(?<i1>\\d)(?<i2>\\d)";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()){
            System.out.println(matcher.group("i1"));
            System.out.println(matcher.group("i2"));
        }
    }
}
// 结果是1,2
  1. 非捕获分组
常用分组构造形式说明
(?:pattern)匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储以后使用的匹配。这对于用“or”字符(|)组合模式部件的情况很有用。例如:`industr(?:y
(?=pattern)它是一个非捕获匹配。例如:`windows(?=95
(?!pattern)该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如:`windows(?!95

 这些正则表达式虽然都带了括号,但他们并不能使用matcher.group()来获取。

  1. 反向引用

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

  1. 去重案例
String content = "hello helloasdas";
String regStr = "(\\w)\\1+";
Pattern compile = Pattern.compile(regStr);
Matcher matcher = compile.matcher(content);
String str = matcher.replaceAll("$1");
System.out.println(str);

2.3 定位符

符号含义示例说明匹配输入
^指定起始字符^[0-9]+[a-z]*以至少1个数字开头,后接任意个小写字 母的字符串123、6aa、555edf
$指定结束字符^[0-9]\\-[a-z]+$以1个数字开头后接连字符“-”,并以至少1个小写字母结尾的字符串1-a
\\b匹配目标字符串的边界han\\b这里说的字符串的边界指的是子串间有空格,或者是目标字符串的结束位置hanshunping sphan nnhan
\\B匹配目标字符串的非边界han\\B和b的含义刚刚相反hanshunping sphan nr

四、正则表达式的三个常用类

1. Pattern

pattern对象是一个正则表达式对象。Pattern类没有公共构造方法。要创建一个 Pattern对象,调用其公共静态方法,它返回一个 Pattern对象。该方法接受一个正则表达式作为它的第一个参数,比如:Pattern r = Pattern.compile(pattem)

📌示例代码:

String content = "18345678901";
String regStr = "(1(?:3|4|5|8))\\d{9}$";
boolean matches = Pattern.matches(regStr, content);
System.out.println(matches);

这里的是整体匹配,即它是从头开始匹配的。但使用matcher.find()是部分匹配。例如:👇

String content = "hello helloasdas";
       String regStr = "hello";
       Pattern pattern = Pattern.compile(regStr);
       Matcher matcher = pattern.matcher(content);
       while (matcher.find()){
           System.out.println(matcher.group(0));
       } // 这里会匹配到两个hello
       boolean matches = Pattern.matches(regStr, content);
       System.out.println(matches); // 这是false,因为它是整体匹配,即正则表达式去匹配整个字符串。
 

matches()的底层也只是封装了find()方法。

2. Matcher

Matcher 对象是对输入字符串进行解释和匹配的引擎。与Pattern类一样, Matcher也没有公共构造方法。你需要调用 Pattern对象的matcher()来获得一个 Matcher对象

方法说明
public int start()返回以前匹配的初始索引
public int start(int group)返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引public int end()返回最后匹配字符之后的偏移量
public int end(int group)返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。(返回的是最后的索引+1)
public boolean lookingAt()尝试将从区域开头开始的输入序列与该模式匹配。
public boolean find()尝试查找与该模式匹配的输入序列的下一个子序列。
public boolean find(int start)重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
public boolean matches()尝试将整个区域与模式匹配。
public Matcher appendReplacement(StringBuffer sb, String replacement)实现非终端添加和替换步骤。
public StringBuffer appendTail(StringBuffer sb)实现终端添加和替换步骤
public String replaceAll(String replacement)替换模式与给定替换字符串相匹配的输入序列的每个子序列。( 不会修改原内容,会返回一个修改后的字符串)
public String replaceFirst(String replacement)替换模式与给定替换字符串匹配的输入序列的第一个子序列。
public static String quoteReplacement(String s)返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement方法一个字面字符串一样工作。
public Matcher appendReplacement(StringBuffer sb, String replacement)实现非终端添加和替换步骤。
public StringBuffer appendTail(StringBuffer sb)实现终端添加和替换步骤。

3. PatternSyntaxException

PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

五、String中使用正则表达式演示

方法说明
public String replacrAll(String regex,String replacement)使用replacement替换字符串中和regex匹配的字符
public boolean matches(String regex)匹配和regex相符的字符串
public String[] split(String regex)分割字符串。

📌演示:

String content = "hello helloasdas";
String str = content.replaceAll("(\\w)\\1+", "$1");
System.out.println(str); // 结果helo heloasdas	
String content = "hehllos";
String regex = "^(h)\\w*s$";	
boolean matches = content.matches("^(h)\\w*s$");
System.out.println(matches); //结果 true
String content = "hehllos";
String[] hs = content.split("h");
for (String h : hs) {
    System.out.println(h);
} // 结果: e	llos

六、总结

 以上就是正则表达式的所有内容。主要介绍了正则表达式的语法使用还有一些底层原理。如果文章中有错误还请留言📫或者私信我。🙏

最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺术留白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值