天天看點

Java正則(3)— 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|false,此标志位預設為false。如果比對範圍不是整個目标字元串,而是一部分,那麼如果此标志位設為true的話,則允許順序環視、逆序環視以及單詞分界符超越比對範圍邊界的設定,比對目标字元串的其他部分,也就是可以稍微有越界行為。可以通過useTransparentBounds()進行修改設定。
public Matcher

useTransparentBounds

(boolean b)
設定TransparentBounds标志位的值

true|false

public boolean hasAnchoringBounds() AnchoringBounds标志位:查詢AnchoringBounds标志位的值,此标志位預設為true。在應用正規表達式的時候,我們可以指定目标字元串的檢索範圍,也就是說在目标字元串的子字元串中應用正規表達式。但此時會有一個問題,那就是 ^ 和 $ 應該比對整個字元串的開頭和結尾呢? 還是檢索範圍的起始和結束位置呢?Java 為我們提供了足夠的靈活性,我們可以通過下面的方法來檢視和設定,預設值是比對檢索範圍的起始和結束位置。
public Matcher

useAnchoringBounds

(boolean b)
設定AnchoringBounds标志位的值

true | false

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, );
        groups = new int[parentGroupCount * ];
        locals = new int[parent.localCount];

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

構造器有包通路權限,可知不能在包外通過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;
    }
           

可知需要通過 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()方法)

變量 類型
first int -1
last int
oldLast int -1
lastAppendPosition int
from int
to int text.length()
groups int[] locals[i] = -1
locals int[] locals[i] = -1
❦ public Matcher reset():重置比對器的狀态
public Matcher reset() {
        first = -;
        last = ;
        oldLast = -;
        for(int i=; i<groups.length; i++)
            groups[i] = -;
        for(int i=; i<locals.length; i++)
            locals[i] = -;
        lastAppendPosition = ;
        from = ;
        to = getTextLength();
        return this;
    }
           

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

變量 類型
first int -1
last int
oldLast int -1
lastAppendPosition int
from int
to int text.length()
groups int[] locals[i] = -1
locals int[] locals[i] = -1
parentPattern Pattern 構造器傳入的Pattern對象
text CharSequence 構造器傳入的目标字元串

測試一:

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
        }
           

測試二:

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
        }
           

由測試1和測試2可知

reset

方法可将 Matcher 對象狀态初始化。

❦ public Matcher reset(CharSequence input):重置比對器的狀态,重置目标字元串的值為input
public Matcher reset(CharSequence input) {
        text = input;
        return reset();
    }
           

可知此方法在

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());
    }
           

輸出:

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

檢視

group()

源碼:

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

可知

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());// ab%12
    }
           
❦ 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+比對到了整個字元串
           

我們現在回頭看一下

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+不能比對前面的aa
           

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());// 傳回false
           
❦ 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());                                                                          
           

說了這麼多,相信大家都明白了以上幾個方法的使用,該說說正規表達式的分組在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()); // 傳回0 傳回第一組比對到的子字元串的第一個字元在原字元串中的索引号   
System.out.println(m.start()); // 傳回3 傳回第二組比對到的子字元串的第一個字元在原字元串中的索引号   
System.out.println(m.end()); // 傳回3 傳回第一組比對到的子字元串的最後一個字元在原字元串中的索引号    
System.out.println(m.end()); // 傳回7                                   
System.out.println(m.group()); // 傳回aaa,傳回第一組比對到的子字元串                 
System.out.println(m.group()); // 傳回2223,傳回第二組比對到的子字元串                
           

現在我們使用一下稍微進階點的正則比對操作,例如有一段文本,裡面有很多數字,而且這些數字是分開的,我們現在要将文本中所有數字都取出來。利用java的正則操作是那麼的簡單。

♨ Java代碼示例:

Pattern p = Pattern.compile("\\d+");                                       
Matcher m = p.matcher("我的QQ是:456456 我的電話是:0532214 我的郵箱是:[email protected]");  
while (m.find()) {                                                         
    System.out.println(m.group());                                         
}                                                                          
           

輸出:

如将以上 while() 循環替換成:

Pattern p = Pattern.compile("\\d+");                                       
Matcher m = p.matcher("我的QQ是:456456 我的電話是:0532214 我的郵箱是:[email protected]");  
while (m.find()) {                                                         
    System.out.println(m.group());                                         
    System.out.print("start:" + m.start());                                
    System.out.println(" end:" + m.end());                                 
}                                                                          
           

則輸出:

start: end:

start: end:

start: end:
           

現在大家應該知道,每次執行比對操作後

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();                    
}                                              
           

♨ 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();                             
}                                                     
           

♨ 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           
           
❦ 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;                                                                    
}                                                                             
           

檢視源碼有

getSubSequence(lastAppendPosition, getTextLength())

,即擷取從

lastAppendPosition

索引處開始,到目的字元串結束索引處之間的子串。

lastAppendPosition 為何值 ?

查閱Matcher類代碼後,發現appendReplacement方法中有:

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後          
           
❦ Mathcer.regionStart()、Mathcer.regionEnd()、Mathcer.region(int start,int end)

♨ Mathcer.regionStart() 方法源碼

傳回比對器區域的起始點索引位置。

public int regionStart() {
    return from;          
}                         
           

♨ Mathcer.regionEnd() 方法源碼

傳回比對器區域的結束點索引位置。

public int regionEnd() {
    return to;          
}                       
           

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

設定目标字元串的比對範圍。

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

從源代碼中可知

region

方法首先調用

reset()

重置,然後對 from 和 to 指派,來設定比對的目的字元串的範圍。

♨ Java代碼示例:

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

輸出:

group():ab%1
regionStart():0
regionEnd():4
           
❦ 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(, 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());
           
Matches starting at character 
Matches starting at character 
           

\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(, 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());
           
Matches starting at character 
Exception in thread "main" java.lang.IllegalStateException: No match available
    at java.util.regex.Matcher.start(Matcher.java:)
    at testRegex.aaa.main(aaa.java:)
           
❦ 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();                                                             
    }                                                                                     
}                                                                                         
           
正則是\d+\b|[><]=?,比對文本是,比對是否成功:true,比對結果是
hitEnd() is true
requireEnd() is true

正則是\d+\b|[><]=?,比對文本是>,比對是否成功:true,比對結果是
hitEnd() is false
requireEnd() is false

正則是\d+\b|[><]=?,比對文本是>,比對是否成功:true,比對結果是>
hitEnd() is true
requireEnd() is false

正則是\d+\b|[><]=?,比對文本是>,比對是否成功:true,比對結果是>
hitEnd() is false
requireEnd() is false

正則是\d+\b|[><]=?,比對文本是>=,比對是否成功:true,比對結果是>=
hitEnd() is false
requireEnd() is false

正則是\d+\b|[><]=?,比對文本是>=,比對是否成功:true,比對結果是>=
hitEnd() is false
requireEnd() is false

           

繼續閱讀