java基礎---->String中replace和replaceAll方法


  這里面我們分析一下replace與replaceAll方法的差異以及原理。

 

replace各個方法的定義

一、replaceFirst方法

public String replaceFirst(String regex, String replacement) {
    return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}

 

二、replace方法

public String replace(CharSequence target, CharSequence replacement) {
    return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}

 

三、replaceAll方法

public String replaceAll(String regex, String replacement) {
    return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}

 

replace各個方法的原理

我們通過以下的例子來分析他們的原理。

@Test
public void stringReplace() {
    replaceFirst("year = 1929. month=07, day=29, other=\\d{2}");
}

public void replaceFirst(String string) {
    System.out.println(string.replaceFirst("\\d{2}", "--"));

    System.out.println(string.replace("\\d{2}", "--"));
    System.out.println(string.replace("29", "--"));

    System.out.println(string.replaceAll("\\d{2}", "--"));
}

// year = --29. month=07, day=29, other=\d{2}
// year = 1929. month=07, day=29, other=--
// year = 19--. month=07, day=--, other=\d{2}
// year = ----. month=--, day=--, other=\d{2}

一、首先我們分析一下replaceFirst與replaceAll方法,他們的區別在於Pattern構建之后Matcher調用的方法不同。一個是reaplceFirst、一個是replaceAll方法。這兩個方法現在可以分析一下。

1、首先對於Matcher的replceFirst方法:可以看到只調用一下的appendReplacement和appendTail方法。關於appendReplacement方法后面可以貼出源碼,實現比較復雜

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

2、對於Matcher的replceAll方法,和上述的replaceFirst方法類似。只不過是多次調用了appendReplacement的替換函數。直到沒有匹配為止

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

 

二、對於replace方法,和上述的replaceAll方法主要有以下兩種區別。

1、在Pattern.compile時,添加了Pattern.LITERAL的flag,表示pattern會把regex當作純文本來處理了。比如\\d{2}不轉義成兩個0-9的數字,而是當作純文本\\d{2}看待。

2、在調用MatcherMatcher.quoteReplacement(replacement.toString())方法對replacement做了對特殊符號($和\)作去除轉義的操作。

public static String quoteReplacement(String s) {
    if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1))
        return s;
    StringBuilder sb = new StringBuilder();
    for (int i=0; i<s.length(); i++) {
        char c = s.charAt(i);
        if (c == '\\' || c == '$') {
            sb.append('\\');
        }
        sb.append(c);
    }
    return sb.toString();
}

但是為何只對\\和$做處理呢? 

 

三、以下是我們的重點appendReplacement方法

 1 public Matcher appendReplacement(StringBuffer sb, String replacement) {
 2 
 3     // If no match, return error
 4     if (first < 0)
 5         throw new IllegalStateException("No match available");
 6 
 7     // Process substitution string to replace group references with groups
 8     int cursor = 0;
 9     StringBuilder result = new StringBuilder();
10 
11     while (cursor < replacement.length()) {
12         char nextChar = replacement.charAt(cursor);
13         if (nextChar == '\\') {
14             cursor++;
15             if (cursor == replacement.length()) 
16                 throw new IllegalArgumentException("character to be escaped is missing");
17             nextChar = replacement.charAt(cursor);
18             result.append(nextChar);
19             cursor++;
20         } else if (nextChar == '$') {
21             // Skip past $
22             cursor++;
23             // Throw IAE if this "$" is the last character in replacement
24             if (cursor == replacement.length()) 
25                 throw new IllegalArgumentException("Illegal group reference: group index is missing");
26             nextChar = replacement.charAt(cursor);
27             int refNum = -1;
28             if (nextChar == '{') {
29                 cursor++;
30                 StringBuilder gsb = new StringBuilder();
31                 while (cursor < replacement.length()) {
32                     nextChar = replacement.charAt(cursor);
33                     if (ASCII.isLower(nextChar) ||
34                         ASCII.isUpper(nextChar) ||
35                         ASCII.isDigit(nextChar)) {
36                         gsb.append(nextChar);
37                         cursor++;
38                     } else {
39                         break;
40                     }
41                 }
42                 if (gsb.length() == 0)
43                     throw new IllegalArgumentException("named capturing group has 0 length name");
44                 if (nextChar != '}')
45                     throw new IllegalArgumentException("named capturing group is missing trailing '}'");
46                 String gname = gsb.toString();
47                 if (ASCII.isDigit(gname.charAt(0)))
48                     throw new IllegalArgumentException("capturing group name {" + gname + "} starts with digit character");
49                 if (!parentPattern.namedGroups().containsKey(gname))
50                     throw new IllegalArgumentException("No group with name {" + gname + "}");
51                 refNum = parentPattern.namedGroups().get(gname);
52                 cursor++;
53             } else {
54                 // The first number is always a group
55                 refNum = (int)nextChar - '0';
56                 if ((refNum < 0)||(refNum > 9))
57                     throw new IllegalArgumentException("Illegal group reference");
58                 cursor++;
59                 // Capture the largest legal group string
60                 boolean done = false;
61                 while (!done) {
62                     if (cursor >= replacement.length()) {
63                         break;
64                     }
65                     int nextDigit = replacement.charAt(cursor) - '0';
66                     if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
67                         break;
68                     }
69                     int newRefNum = (refNum * 10) + nextDigit;
70                     if (groupCount() < newRefNum) {
71                         done = true;
72                     } else {
73                         refNum = newRefNum;
74                         cursor++;
75                     }
76                 }
77             }
78             // Append group
79             if (start(refNum) != -1 && end(refNum) != -1)
80                 result.append(text, start(refNum), end(refNum));
81         } else {
82             result.append(nextChar);
83             cursor++;
84         }
85     }
86     // Append the intervening text
87     sb.append(text, lastAppendPosition, first);
88     // Append the match substitution
89     sb.append(result);
90 
91     lastAppendPosition = last;
92     return this;
93 }

 

四、以下是appendTail的代碼

public StringBuffer appendTail(StringBuffer sb) {
    sb.append(text, lastAppendPosition, getTextLength());
    return sb;
}

 

友情鏈接

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM