String.replaceAll方法,正則妙用

更多精彩文章。java

《微服務不是所有,只是特定領域的子集》程序員

《「分庫分表" ?選型和流程要慎重,不然會失控》正則表達式

這麼多監控組件,總有一款適合你vim

《使用Netty,咱們到底在開發些什麼?》bash

《這多是最中肯的Redis規範了》微信

《程序員畫像,十年沉浮》app

最有用系列:微服務

《Linux生產環境上,最經常使用的一套「vim「技巧》post

《Linux生產環境上,最經常使用的一套「Sed「技巧》ui

《Linux生產環境上,最經常使用的一套「AWK「技巧》

若是你認同這些知識,歡迎關注微信公衆號小姐姐味道

ID:xjjdog


我一般是不太關心代碼的具體實現的,由於個人開發語言很雜,傾向於一些最簡單通用的方式去解決。今兒不當心在羣裏看到一位朋友發了下面的java代碼,感受本身仍是很侷限很無知的:

String str1 = "createTime";
String str2 = "createTimeAt";
String regex = "([A-Z])+";

System.out.println(str1.replaceAll(regex, "_$1").toLowerCase());
System.out.println(str2.replaceAll(regex, "_$1").toLowerCase());

//result
//create_time
//create_time_at
複製代碼

經過輸出能夠看到,這段代碼的做用是把駝峯命名格式的字符串替換成下劃線分割,這個功能比較簡單,可是吸引個人倒是他的代碼。

"createTime".replaceAll("([A-Z]+)","_$1")
複製代碼

這行代碼簡單的很,就是調用了String類的replaceAll方法,方法的第一個參數是正則表達式,第二個參數是將要被替換成的新值。

讓我驚奇的是他代碼中,replaceAll的第二個參數,也就是JDK文檔中名爲replacement的參數,居然是_$1。這是什麼鬼?還支持相似佔位符這樣的東西?我一直都不知道。

問題探索

因爲以前研究過一段正則表達式,經過觀察replaceAll的第一個參數([A-Z]+),我猜測,這個應該是用到了正則表達式的分組,對應JDK中,就是java.util.regex.Matcher類的group()方法。

在Linux的Sed命令上,就使用&進行了一些替換,道理應該是相通的。

因而看了下String.replaceAll方法是如何實現的。 JDK:

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

哦,原來它底層就是用了Matcher,只不過用的是Matcher本身的replaceAll方法。 去看它的文檔,這個方法的參數果真有鬼,看下面實現代碼。

public String replaceAll(String replacement) {
        reset();
        boolean result = find();
        if (result) {
            StringBuilder sb = new StringBuilder();
            do {
                appendReplacement(sb, replacement);
                result = find();
            } while (result);
            appendTail(sb);
            return sb.toString();
        }
        return text.toString();
    }
複製代碼

裏面關鍵的部分就是文檔中說的appendReplacement方法,而後能夠看到詳細的描述文檔。

看到這裏明白了,原來這個方法的replacement參數能夠經過$字符來指代Matcher經過正則匹配獲得的分組,支持name和number 兩種方式,這裏對應的就是Matcher類的group(name)和group(int)兩個方法。

結論

一、String的replaceAll方法其實是經過java.util.regex.Matcher類的replaceAll()方法實現的。
二、java.util.regex.Matcher類的replaceAll方法又是經過調用appendReplacement方法實現替換邏輯 三、Matcher類的appendReplacement方法的replacement參數支持經過$符號來指代Matcher匹配的分組

下面這串代碼,就是使用Matcher類分組的一個最佳實踐。

String data = "哈哈哈,xjjdog的手機號碼是:12345678901,你會打給我嗎";
//經過Matcher的分組功能,能夠提取出上面字符串中的手機號
Matcher matcher = Pattern.compile(".*(個人手機號碼是:([0-9]{11}))").matcher(data);
while (matcher.find()) {
    System.out.println("G0:" + matcher.group(0));
    System.out.println("G1:" + matcher.group(1));
    System.out.println("G2:" + matcher.group(2));
}
//result
//G0:哈哈哈,xjjdog的手機號碼是:12345678901
//G1:xjjdog的手機號碼是:12345678901
//G2:12345678901
複製代碼

group(0)表示整個字符串
group(1)表示第一個匹配的,上面的例子中就是(個人手機號碼是:([0-9]{11}))部分
group(2)表示第二個匹配的,上面的例子中就是([0-9]{11})部分

使用分組能夠用來提取字符串中的目標字符串值,很好用!

幾個例子

下面是幾個例子,你們能夠舉一反三。

駝峯轉下劃線命名

public static String camelToUnderline(String camelName) {
return camelName.replaceAll("([A-Z]+)", "_$1").toLowerCase();
}
複製代碼

下劃線轉駝峯

這個稍微麻煩點,是模仿者Matcher.replaceAll方法寫的。

public static String underlineToCamel(String underlineName) {
        Matcher matcher = Pattern.compile("(_[a-z]{1})").matcher(underlineName);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String replacement = matcher.group(1);
            matcher.appendReplacement(result, replacement.replace("_", "").toUpperCase());
        }
        matcher.appendTail(result);
        return result.toString();
}
複製代碼

另外,Mybatis Generator插件源碼中的也提供了相似方法(JavaBeansUtil.getCamelCaseString),這裏作了下簡單修改

public static String getCamelCaseString(String inputString) {
        StringBuilder sb = new StringBuilder();
        boolean nextUpperCase = false;
        for (int i = 0; i < inputString.length(); i++) {
            char c = inputString.charAt(i);
            switch (c) {
                case '_':
                case '-':
                case '@':
                case '$':
                case '#':
                case ' ':
                case '/':
                case '&':
                    if (sb.length() > 0) {
                        nextUpperCase = true;
                    }
                    break;
                default:
                    if (nextUpperCase) {
                        sb.append(Character.toUpperCase(c));
                        nextUpperCase = false;
                    } else {
                        sb.append(Character.toLowerCase(c));
                    }
                    break;
            }
        }
        return sb.toString();
    }
複製代碼

沒有複雜的正則參與,速度顯而快了很多。

End

看一些優秀的開源代碼,確實可以瞭解到一些實用的技巧。這比起本身費勁心力重複製造一些輪子,要高效的多。時間要用在刀刃上,但不是用來切豆腐。

相關文章
相關標籤/搜索