Matcher 详解

☪ Matcher 概述

声明:public final class Matcher extends Object implements MatchResult

Matcher 类有final 修饰,可知他不能被子类继承

含义:匹配器类,通过解释 Pattern 对 character sequence 执行匹配操作的引擎。

注意:此类的实例用于多个并发线程是不安全的。

☪ Matcher 方法

方法说明
public Pattern pattern() ⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢返回创建Matcher对象的Pattern对象。
public MatchResult toMatchResult()将匹配结果以MatchResult的形式返回
public Matcher usePattern(Pattern newPattern)修改Matcher对象的Pattern,用以进行新的模式匹配。
public Matcher reset()重置匹配器的状态。
public Matcher reset(CharSequence input)重置匹配器的状态,重置目标字符串的值为input。
public int start()返回当前匹配到的字符串在原目标字符串中的起始索引位置
public int start(int group)返回当前匹配到的字符串中group组在目标字符串的起始索引位置
public int end()返回当前匹配的字符串的最后一个字符在原目标字符串中的offset(偏移量),这个需要大家注意一下。
public int end(int group)返回当前匹配的字符串中group组的最后一个字符在原目标字符串中的offset(偏移量),这个需要大家注意一下。
public String group()返回匹配到的字符串,结合find函数使用。
public String group(int group)返回匹配到的字符串中的group组的字符串。
public String group(String name)返回被named-capturing组捕获的字符串,关于named-capturing group(命名捕获组)是JDK1.7新增的功能,可以将正则表达式中的组进行命名。
public int groupCount()返回当前Matcher对象捕获的组的个数。
public boolean matches()将整个目标字符串与正则表达式进行匹配,只有完全匹配才能返回true,否则false。
public boolean find()对目标字符串进行正则匹配,通过while可以多次执行find方法,获取多次的匹配结果,代码编写方式类似于iterator.next()。
public boolean find(int start)在指定的索引位置对目标字符串进行正则匹配。
public boolean lookingAt()目标字符串的起始字符串与正则表达式匹配返回true,否则返回false。
public static String quoteReplacement(String s)返回字符串s字面意义的替代字符串。
public Matcher appendReplacement(StringBuffer sb, String replacement)向sb中追加replacement字符串,replacement字符串中可以包含匹配器中的分组参数,如1,2。
public StringBuffer appendTail(StringBuffer sb)将Matcher匹配后的尾部字符串追加至sb中。
public String replaceAll(String replacement)将目标字符串中所有满足正则匹配的字符串替换为replacement。
public String replaceFirst(String replacement)将目标字符串中第一个满足正则匹配的字符串替换为replacement。
public Matcher region(int start, int end)设置目标字符串的匹配范围。
public int regionStart()返回匹配器区域的起始点索引位置。
public int regionEnd()返回匹配器区域的结束点索引位置。
public boolean hasTransparentBounds()TransparentBounds标志位:查询TransparentBounds标志位true
public Matcher useTransparentBounds(boolean b)设置TransparentBounds标志位的值`true
public boolean hasAnchoringBounds()AnchoringBounds标志位:查询AnchoringBounds标志位的值,此标志位默认为true。在应用正则表达式的时候,我们可以指定目标字符串的检索范围,也就是说在目标字符串的子字符串中应用正则表达式。但此时会有一个问题,那就是 ^ 和 $ 应该匹配整个字符串的开头和结尾呢? 还是检索范围的起始和结束位置呢?Java 为我们提供了足够的灵活性,我们可以通过下面的方法来查看和设置,默认值是匹配检索范围的起始和结束位置。
public Matcher useAnchoringBounds(boolean b)设置AnchoringBounds标志位的值`true
public boolean hitEnd()
public boolean requireEnd()
boolean search(int from)
boolean match(int from, int anchor)
int getTextLength()返回目标字符串的长度。
CharSequence getSubSequence(int beginIndex, int endIndex)获取目标字符串的子字符串。
char charAt(int i)返回目标字符串中索引为i的字符
☪ 方法示例

❦ Matcher 构造方法

    /**
     * No default constructor.
     */
    Matcher() {
    }

    /**
     * All matchers have the state used by Pattern during a match.
     */
    Matcher(Pattern parent, CharSequence text) {
        this.parentPattern = parent;
        this.text = text;

        // Allocate state storage
        int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
        groups = new int[parentGroupCount * 2];
        locals = new int[parent.localCount];

        // Put fields into initial states
        reset();
    }123456789101112131415161718192021

构造器有包访问权限,可知不能在包外通过new创建Matcher对象。

如何在自定义的包中得到Matcher类的实例?

Matcher类中没有合适的方法,查阅Pattern类有:

    public Matcher matcher(CharSequence input) {
    if (!compiled) {
        synchronized(this) {
        if (!compiled)
            compile();
        }
    }
        Matcher m = new Matcher(this, input);
        return m;
    }12345678910

可知需要通过 Pattern 对象调用 matcher 方法来返回 Matcher 类的实例。

对照 Matcher 构造器源码,可知构造器将 Pattern 对象的引用赋于 Matcher 中变量 parentPattern,目标字符串赋于变量 text;并创建了数组 groups 和 locals 。

数组 groups 是组使用的存储。存储的是当前匹配的各捕获组的 first 和 last 信息。

groups[0] 存储的是组 0 的 first,groups[1] 存储的是组 0 的 last,groups[2] 存储的是组 1 的 first,groups[3] 存储的是组 1 的 last,依次类推。

初始化后状态表:(具体分析见以下reset()方法)

变量类型
firstint-1
lastint0
oldLastint-1
lastAppendPositionint0
fromint0
tointtext.length()
groupsint[]locals[i] = -1
localsint[]locals[i] = -1
❦ public Matcher reset():重置匹配器的状态

    public Matcher reset() {
        first = -1;
        last = 0;
        oldLast = -1;
        for(int i=0; i<groups.length; i++)
            groups[i] = -1;
        for(int i=0; i<locals.length; i++)
            locals[i] = -1;
        lastAppendPosition = 0;
        from = 0;
        to = getTextLength();
        return this;
    }12345678910111213

可知 reset() 方法改变了变量first 、last 、oldLast、lastAppendPosition、from、to的值并将数组groups、locals初始化。

变量类型
firstint-1
lastint0
oldLastint-1
lastAppendPositionint0
fromint0
tointtext.length()
groupsint[]locals[i] = -1
localsint[]locals[i] = -1
parentPatternPattern构造器传入的Pattern对象
textCharSequence构造器传入的目标字符串

测试一:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
        Matcher m = p.matcher("ab%12-cd%34");
        if (m.find()) {
            System.out.println("开始索引:" + m.start());// 开始索引:0
            System.out.println("group():" + m.group());// group():ab%12
        }
        if (m.find()) {
            System.out.println("开始索引:" + m.start());// 开始索引:6
            System.out.println("group():" + m.group());// group():cd%34
        }12345678910

测试二:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
        Matcher m = p.matcher("ab%12-cd%34");
        if (m.find()) {
            System.out.println("开始索引:" + m.start());// 开始索引:0
            System.out.println("group():" + m.group());// group():ab%12
        }
        m.reset();
        if (m.find()) {
            System.out.println("开始索引:" + m.start());// 开始索引:0
            System.out.println("group():" + m.group());// group():ab%12
        }
123456789101112

由测试1和测试2可知reset方法可将 Matcher 对象状态初始化。

❦ public Matcher reset(CharSequence input):重置匹配器的状态,重置目标字符串的值为input

    public Matcher reset(CharSequence input) {
        text = input;
        return reset();
    }1234

可知此方法在reset()方法的基础上改变了目标字符串的值。

Java代码示例:

    Pattern p = Pattern.compile("(\\w+)%(\\d+)");
    Matcher m = p.matcher("ab%12-cd%34");
    m.reset("ef%56-gh%78");
    while (m.find()) {
        System.out.println("group():" + m.group());
    }123456

输出:

    group():ef%56
    group():gh%7812
❦ public String group()

查看group()源码:

    public String group() {
        return group(0);
    }123

可知 group() 实际调用了 group(int group) 方法,参数 group 为 0。组零表示整个模式。

Java代码示例:

    Pattern p = Pattern.compile("(\\w+)%(\\d+)");
    Matcher m = p.matcher("ab%12-cd%34");
    if (m.find()) {
        System.out.println(m.group());// ab%12
        System.out.println(m.group(0));// ab%12
    }123456
❦ Matcher.matches()、Matcher.lookingAt()、Matcher.find()

Matcher 类提供了三个匹配操作方法,三个方法均返回 boolean 类型,当匹配到时返回 true,没匹配到则返回 false 。

matches() 对整个字符串进行匹配,只有整个字符串都匹配了才返回true 。

Java代码示例:

Pattern p = Pattern.compile("\\d+");

Matcher m = p.matcher("22bb23");
System.out.println(m.matches());// 返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功.

m = p.matcher("2223");
System.out.println(m.matches());// 返回true,因为\d+匹配到了整个字符串1234567

我们现在回头看一下Pattern.matcher(String regex,CharSequence input),它与下面这段代码等价
Pattern.compile(regex).matcher(input).matches()

lookingAt() 对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true。

Java代码示例::

Pattern p = Pattern.compile("\\d+");

Matcher m = p.matcher("22bb23");
System.out.println(m.lookingAt());// 返回true,因为\d+匹配到了前面的22

m = p.matcher("aa2223");
System.out.println(m.lookingAt());// 返回false,因为\d+不能匹配前面的aa1234567

find()对字符串进行匹配,匹配到的字符串可以在任何位置。

Java代码示例:

Pattern p = Pattern.compile("\\d+");

Matcher m = p.matcher("22bb23");
System.out.println(m.find());// 返回true

m = p.matcher("aa2223");
System.out.println(m.find());// 返回true

m = p.matcher("aa2223bb");
System.out.println(m.find());// 返回true

m = p.matcher("aabb");
System.out.println(m.find());// 返回false12345678910111213
❦ Mathcer.start()、Matcher.end()、Matcher.group()

当使用matches()lookingAt()find()执行匹配操作后,就可以利用以上三个方法得到更详细的信息:

  • start()返回匹配到的子字符串的第一个字符在原字符串中的索引位置;
  • end()返回匹配到的子字符串的最后一个字符在原字符串中的索引位置;
  • group()返回匹配到的子字符串。

Java代码示例:

Pattern p = Pattern.compile("\\d+");                                                                    

System.out.println("==========find 方法==========");                                                      
Matcher m = p.matcher("aaa2223bb");                                                                     
System.out.println(m.find());// 匹配2223                                                                  
System.out.println(m.start());// 返回3                                                                    
System.out.println(m.end());// 返回7,返回的是2223后的索引号                                                        
System.out.println(m.group());// 返回2223                                                                 

System.out.println("==========lookingAt 方法==========");                                                 
m = p.matcher("2223bb");                                                                                
System.out.println(m.lookingAt()); // 匹配2223                                                            
System.out.println(m.start()); // 返回0,由于lookingAt()只能匹配前面的字符串,所以当使用lookingAt()匹配时,start()方法总是返回0        
System.out.println(m.end()); // 返回4                                                                     
System.out.println(m.group()); // 返回2223                                                                

System.out.println("==========matches 方法==========");                                                   
m = p.matcher("2223bb");                                                                                
System.out.println(m.matches()); // 匹配整个字符串,返回false,所以后边的执行会报错                                          
System.out.println(m.start());                                                                          
System.out.println(m.end());                                                                            
System.out.println(m.group());                                                                          12345678910111213141516171819202122

说了这么多,相信大家都明白了以上几个方法的使用,该说说正则表达式的分组在java中是怎么使用的。

❦ start(int i)、end(int i),group(int i)、groupCount()

start()end()group()均有一个重载方法,它们是start(int i)end(int i)group(int i)专用于分组操作,Mathcer 类还有一个groupCount()用于返回有多少组。

Java代码示例:

Pattern p = Pattern.compile("([a-z]+)(\\d+)");                         
Matcher m = p.matcher("aaa2223bb");                                    

System.out.println(m.find()); // 匹配aaa2223                             
System.out.println(m.groupCount()); // 返回2,因为有2组                       
System.out.println(m.start(1)); // 返回0 返回第一组匹配到的子字符串的第一个字符在原字符串中的索引号   
System.out.println(m.start(2)); // 返回3 返回第二组匹配到的子字符串的第一个字符在原字符串中的索引号   
System.out.println(m.end(1)); // 返回3 返回第一组匹配到的子字符串的最后一个字符在原字符串中的索引号    
System.out.println(m.end(2)); // 返回7                                   
System.out.println(m.group(1)); // 返回aaa,返回第一组匹配到的子字符串                 
System.out.println(m.group(2)); // 返回2223,返回第二组匹配到的子字符串                1234567891011

现在我们使用一下稍微高级点的正则匹配操作,例如有一段文本,里面有很多数字,而且这些数字是分开的,我们现在要将文本中所有数字都取出来。利用java的正则操作是那么的简单。

Java代码示例:

Pattern p = Pattern.compile("\\d+");                                       
Matcher m = p.matcher("我的QQ是:456456 我的电话是:0532214 我的邮箱是:aaa123@aaa.com");  
while (m.find()) {                                                         
    System.out.println(m.group());                                         
}                                                                          12345

输出:

456456
0532214
123123

如将以上 while() 循环替换成:

Pattern p = Pattern.compile("\\d+");                                       
Matcher m = p.matcher("我的QQ是:456456 我的电话是:0532214 我的邮箱是:aaa123@aaa.com");  
while (m.find()) {                                                         
    System.out.println(m.group());                                         
    System.out.print("start:" + m.start());                                
    System.out.println(" end:" + m.end());                                 
}                                                                          1234567

则输出:

456456
start:6 end:12
0532214
start:19 end:26
123
start:36 end:39123456

现在大家应该知道,每次执行匹配操作后start()end()group()三个方法的值都会改变,改变成匹配到的子字符串的信息,以及它们的重载方法,也会改变成相应的信息。

注意:只有当匹配操作成功,才可以使用start()end()group()三个方法,否则会抛出java.lang.IllegalStateException,也就是当matches()lookingAt()find()其中任意一个方法返回 true 时,才可以使用。

❦ Mathcer.replaceAll(String replacement) 和 Mathcer.replaceFirst(String replacement)

Mathcer.replaceAll(String replacement) 方法源码

查看源码可知此方法首先重置匹配器,然后判断是否有匹配,若有,则创建StringBuffer 对象,然后循环调用appendReplacement 方法进行替换,最后调用 appendTail 方法并返回 StringBuffer 对象的字符串形式。

public String replaceAll(String replacement) { 
    reset();                                   
    boolean result = find();                   
    if (result) {                              
        StringBuffer sb = new StringBuffer();  
        do {                                   
            appendReplacement(sb, replacement);
            result = find();                   
        } while (result);                      
        appendTail(sb);                        
        return sb.toString();                  
    }                                          
    return text.toString();                    
}                                              1234567891011121314

Mathcer.replaceFirst(String replacement) 方法源码

查看源码可知此方法其实是 replaceAll方法的减配版本,只对第一次匹配做了替换。

public String replaceFirst(String replacement) {      
    if (replacement == null)                          
        throw new NullPointerException("replacement");
    StringBuffer sb = new StringBuffer();             
    reset();                                          
    if (find())                                       
        appendReplacement(sb, replacement);           
    appendTail(sb);                                   
    return sb.toString();                             
}                                                     12345678910

Java代码示例:

System.out.println("=============例子一============");                
Pattern pattern = Pattern.compile("Java");                         
Matcher matcher = pattern.matcher("JavaJava");                     
System.out.println(matcher.replaceAll("Python"));// 返回PythonPython 
System.out.println(matcher.replaceFirst("python"));// 返回PythonJava 

System.out.println("=============例子二============");                
pattern = Pattern.compile("(\\w+)%(\\d+)");                        
matcher = pattern.matcher("ab%12-cd%34");                          
System.out.println(matcher.replaceAll("app"));// app-app           12345678910
❦ Mathcer.appendReplacement(StringBuffer sb, String replacement) 和 Mathcer.appendTail(StringBuffer sb)

Mathcer.appendReplacement(StringBuffer sb, String replacement) 方法

将当前匹配子串替换为指定字符串,并将从上次匹配结束后到本次匹配结束后之间的字符串添加到一个StringBuffer对象中,最后返回其字符串表示形式。

注意:对于最后一次匹配,其后的字符串并没有添加入StringBuffer对象中,若需要这部分的内容需要使用appendTail方法。

Mathcer.appendTail(StringBuffer sb) 方法源码

将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。

public StringBuffer appendTail(StringBuffer sb) {                             
    sb.append(getSubSequence(lastAppendPosition, getTextLength()).toString());
return sb;                                                                    
}                                                                             1234

查看源码有getSubSequence(lastAppendPosition, getTextLength()),即获取从lastAppendPosition索引处开始,到目的字符串结束索引处之间的子串。

lastAppendPosition 为何值 ?

查阅Matcher类代码后,发现appendReplacement方法中有:

lastAppendPosition = last;1

last 即目前最后一次匹配结束后的索引。

Java代码示例:

Pattern p = Pattern.compile("(\\w+)%(\\d+)")
Matcher m = p.matcher("前ab%12中cd%34后");     
StringBuffer s = new StringBuffer();        
while (m.find()) {                          
    m.appendReplacement(s, "app");          
}                                           
System.out.println(s);// 前app中app           
m.appendTail(s);                            
System.out.println(s);// 前app中app后          123456789
❦ Mathcer.regionStart()、Mathcer.regionEnd()、Mathcer.region(int start,int end)

Mathcer.regionStart() 方法源码

返回匹配器区域的起始点索引位置。

public int regionStart() {
    return from;          
}                         123

Mathcer.regionEnd() 方法源码

返回匹配器区域的结束点索引位置。

public int regionEnd() {
    return to;          
}                       123

Mathcer.region(int start,int end) 方法源码

设置目标字符串的匹配范围。

public Matcher region(int start, int end) {                
    if ((start < 0) || (start > getTextLength()))          
        throw new IndexOutOfBoundsException("start");      
    if ((end < 0) || (end > getTextLength()))              
        throw new IndexOutOfBoundsException("end");        
    if (start > end)                                       
        throw new IndexOutOfBoundsException("start > end");
    reset();                                               
    from = start;                                          
    to = end;                                              
    return this;                                           
}                                                          123456789101112

从源代码中可知region方法首先调用reset()重置,然后对 from 和 to 赋值,来设置匹配的目的字符串的范围。

Java代码示例:

Pattern p = Pattern.compile("(\\w+)%(\\d+)");              
Matcher m = p.matcher("ab%12-cd%34");                      
m.region(0, 4);                                            
while (m.find()) {                                         
    System.out.println("group():" + m.group());            
    System.out.println("regionStart():" + m.regionStart());
    System.out.println("regionEnd():" + m.regionEnd());    
}                                                          12345678

输出:

group():ab%1
regionStart():0
regionEnd():4123
❦ Mathcer.useTransparentBounds(boolean b)

设置TransparentBounds标志位的值true|false

Java代码示例:

String regex = "\\bcar\\b";                                      
String text = "Madagascar is best seen by car or bike.";         
Matcher m = Pattern.compile(regex).matcher(text);                
m.region(7, text.length());                                      
m.useTransparentBounds(false);                                   
m.find();                                                        
System.out.println("Matches starting at character " + m.start());

m.reset();                                                       
m.useTransparentBounds(true);                                    
m.find();                                                        
System.out.println("Matches starting at character " + m.start());123456789101112
Matches starting at character 7
Matches starting at character 2712

\b 匹配一个字边界,即字与空格间的位置。例如,er/b匹配never中的er,但不匹配verb中的er

  • TransparentBounds = false,region区域从index=7开始,Madagascar也就是从car开始,匹配器无法感知region区域外的字符,因此第一个car被匹配。
  • TransparentBounds = true,region区域从index=7开始,Madagascar也就是从car开始,匹配器可以感知region区域外的字符,因此第一个car不被匹配。
❦ Mathcer.useTransparentBounds(boolean b)

AnchoringBounds标志位:

  • 设置AnchoringBounds标志位的值,此标志位默认为true;
  • true:^ 和 $ 应该匹配检索范围的起始和结束位置;
  • false:^ 和 $ 应该匹配整个字符串的开头和结尾;

Java代码示例:

String text = "Madagascar is best seen by car or bike.";         
Matcher m = Pattern.compile("^car").matcher(text);               
m.region(7, text.length());                                      

m.useAnchoringBounds(true);                                      
m.find();                                                        
System.out.println("Matches starting at character " + m.start());

m.reset();                                                       

m.useAnchoringBounds(false);                                     
m.find();                                                        
System.out.println("Matches starting at character " + m.start());12345678910111213
Matches starting at character 7
Exception in thread "main" java.lang.IllegalStateException: No match available
    at java.util.regex.Matcher.start(Matcher.java:342)
    at testRegex.aaa.main(aaa.java:21)1234
❦ Mathcer.hitEnd() 和 Mathcer.requireEnd()

其实这两个方法没搞明白是什么意思,等有时间在搞清楚吧!!!!

String[] matcherStrs = { "1234", "1234.>.567", ">", ">.567", ">=", ">=.567", "oops" };    
Pattern pattern = Pattern.compile("\\d+\\b|[><]=?");                                      
Matcher m = null;                                                                         
for (String matcherStr : matcherStrs) {                                                   
    m = pattern.matcher(matcherStr);                                                      
    boolean find_result = true;                                                           
    if (find_result = m.find()) {                                                         
        System.out.println(String.format("正则是%s,匹配文本是%s,匹配是否成功:%s,匹配结果是%s",               
                m.pattern(), matcherStr, find_result, find_result ? m.group() : "匹配失败")); 
        System.out.println("hitEnd() is " + m.hitEnd());                                  
        System.out.println("requireEnd() is " + m.requireEnd());                          
        System.out.println();                                                             
    }                                                                                     
}                                                                                         1234567891011121314
正则是\d+\b|[><]=?,匹配文本是1234,匹配是否成功:true,匹配结果是1234
hitEnd() is true
requireEnd() is true

正则是\d+\b|[><]=?,匹配文本是1234.>.567,匹配是否成功:true,匹配结果是1234
hitEnd() is false
requireEnd() is false

正则是\d+\b|[><]=?,匹配文本是>,匹配是否成功:true,匹配结果是>
hitEnd() is true
requireEnd() is false

正则是\d+\b|[><]=?,匹配文本是>.567,匹配是否成功:true,匹配结果是>
hitEnd() is false
requireEnd() is false

正则是\d+\b|[><]=?,匹配文本是>=,匹配是否成功:true,匹配结果是>=
hitEnd() is false
requireEnd() is false

正则是\d+\b|[><]=?,匹配文本是>=.567,匹配是否成功:true,匹配结果是>=
hitEnd() is false
requireEnd() is false

12345678910111213141516171819202122232425
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值