溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java正則表達式中如何實現(xiàn)分組和替換

發(fā)布時間:2021-11-30 12:55:54 來源:億速云 閱讀:591 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下Java正則表達式中如何實現(xiàn)分組和替換,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

    正則表達式的子表達式(分組)不是很好懂,但卻是很強大的文本處理工具。

    1 正則表達式熱身

    匹配電話號碼

    // 電話號碼匹配
    // 手機號段只有 13xxx 15xxx 18xxxx 17xxx
    System.out.println("18304072984".matches("1[3578]\\d{9}"));   // true
    
    // 座機號:010-65784236,0316-3312617,022-12465647,03123312336
    String regex = "0\\d{2}-?\\d{8}|0\\d{3}-?\\d{7}";
    String telStr = "010-43367458";
    System.out.println(telStr.matches(regex));  // true

    匹配郵箱

    String mail = "i@jiaobuchong.com.cn";
    String reg = "[a-zA-Z_0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+){1,2}";
    System.out.println(mail.matches(reg));  // true

    特殊字符替換

    將不是中文的字符替換為空:

    String input = "神探狄仁&*%$杰之四大天王@bdfbdbdfdgds23532";
    String reg = "[^\\u4e00-\\u9fa5]";
    input = input.replaceAll(reg, "");
    System.out.println(input);   // 神探狄仁杰之四大天王

    漢字的Unicode編碼范圍是:\u4e00-\u9fa5

    2 分組

    組是用括號劃分的正則表達式,可以根據(jù)組的編號來引用某個組。組號為 0 表示整個表達式,組號 1 表示第一對括號擴起的組,以此類推。

    看 Java API 中 Pattern 中的描述:

    Capturing groups are numbered by counting their opening parentheses from left to right. In the expression ((A)(B(C))), for example, there are four such groups:

    1. ((A)(B(C)))
    2. (A)
    3. (B(C))
    4. (C)

    再比如 A(B(C))D 有三個組:組 0 是 ABCD,組 1 是 BC,組 2 是 C,

    可以根據(jù)有多少個左括號來來確定有多少個分組,括號里的表達式都稱子表達式。

    Eg1:

    Matcher 對象提供很多方法:

    • goupCount() 返回該正則表達式模式中的分組數(shù)目,對應于「左括號」的數(shù)目

    • group(int i) 返回對應組的匹配字符,沒有匹配到則返回 null

    • start(int group) 返回對應組的匹配字符的起始索引

    • end(int group) 返回對應組的匹配字符的最后一個字符索引加一的值

    // 這個正則表達式有兩個組,
    // group(0) 是 \\$\\{([^{}]+?)\\}
    // group(1) 是 ([^{}]+?)
    String regex = "\\$\\{([^{}]+?)\\}";
    Pattern pattern = Pattern.compile(regex);
    String input = "${name}-babalala-${age}-${address}";
    
    Matcher matcher = pattern.matcher(input);
    System.out.println(matcher.groupCount());
    // find() 像迭代器那樣向前遍歷輸入字符串
    while (matcher.find()) {
        System.out.println(matcher.group(0) + ", pos: "
                + matcher.start() + "-" + (matcher.end() - 1));
        System.out.println(matcher.group(1) + ", pos: " +
                matcher.start(1) + "-" + (matcher.end(1) - 1));
    }

    輸出:

    1
    ${name}, pos: 0-6
    name, pos: 2-5
    ${age}, pos: 17-22
    age, pos: 19-21
    ${address}, pos: 24-33
    address, pos: 26-32

    group翻譯成中文就是分組。

    group()或group(0)對應于整個正則表達式每次匹配到的內(nèi)容,

    group(1)表示括號中(一個子表達式分組)匹配到的內(nèi)容。

    Eg2:

    為了更直觀的看分組,在 Eg1 的正則表達式上再多加一對括號:

    String regex = "(\\$\\{([^{}]+?)\\})";
    Pattern pattern = Pattern.compile(regex);
    String input = "${name}-babalala-${age}-${address}";
    
    Matcher matcher = pattern.matcher(input);
    // matcher.find() 方法會對 input 這個字符串多次進行匹配,如果能匹配到,這個匹配結(jié)果里就會包含多個分組,我們可以從分組里提取我們想要的結(jié)果
    while (matcher.find()) {
        System.out.println(matcher.group(0) + ", pos: " + matcher.start());
        System.out.println(matcher.group(1) + ", pos: " + matcher.start(1));
        System.out.println(matcher.group(2) + ", pos: " + matcher.start(2));
    }

    輸出:

    ${name}, pos: 0
    ${name}, pos: 0
    name, pos: 2
    ${age}, pos: 17
    ${age}, pos: 17
    age, pos: 19
    ${address}, pos: 24
    ${address}, pos: 24
    address, pos: 26

    由此可得出一對括號一個分組,可以通過左括號數(shù)來確定有多少個分組。

    通過group()獲取分組中的匹配字串應用場景很廣泛,

    在筆者的一個項目中,通過使用這個特性實現(xiàn)了很有意思的通配符替換,感動!

    Eg3(通過分組提取想要的數(shù)據(jù)):

    // 這個正則表達式會提取字符串中的「數(shù)字」和「字母」
            Pattern pattern = Pattern.compile("([0-9]+).*?([a-zA-Z]+)");
            String input = "那就20200719這樣吧sunny。。。。。。。122432該拿什么與眼淚抗衡twinkle";
            Matcher matcher = pattern.matcher(input);
            // 每個匹配到的子串分組的個數(shù)
            int group = matcher.groupCount();
            // 如果輸入串有多個可被匹配的子串,這里會多次進行匹配
            while (matcher.find()) {
                System.out.println("匹配到的子串:" + matcher.group());  // 匹配到的子串
                for (int i = 1; i <= group; i++) {
                    System.out.println("分組" + i + ": " + matcher.group(i));
                }
            }

    輸出:

    匹配到的子串:20200719這樣吧sunny
    分組1: 20200719
    分組2: sunny
    匹配到的子串:122432該拿什么與眼淚抗衡twinkle
    分組1: 122432
    分組2: twinkle

    3 分組替換

    Eg1:

    String tel = "18304072984";
    // 括號表示組,被替換的部分$n表示第n組的內(nèi)容
    tel = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    System.out.print(tel);   // output: 183****2984

    replaceAll 是一個替換字符串的方法,正則表達式中括號表示一個分組,replaceAll 的參數(shù) 2 中可以使用 $n(n 為數(shù)字)來依次引用子表達式中匹配到的分組字串,"(\\d{3})\\d{4}(\\d{4})", "$1****$2",分為前(前三個數(shù)字)中間四個數(shù)字(最后四個數(shù)字) 替換為(第一組數(shù)字保持不變 $1)(中間為 * )(第二組數(shù)字保持不變 $2)。

    Eg2:

    String one = "hello girl hi hot".replaceFirst("(\\w+)\\s+(\\w+)", "$2 $1"); 
    String two = "hello girl hi hot".replaceAll("(\\w+)\\s+(\\w+)", "$2 $1"); 
    System.out.println(one);   // girl hello hi hot
    System.out.println(two);   // girl hello hot hi

    理解了Eg1,這個例子也自然就理解了。

    Eg3:

    來一個實用的例子,重復標點符號替換:

    String input = "假如生活欺騙了你,,,相信吧,,,快樂的日子將會來臨?。。 ?quot;;
    
    // 重復標點符號替換
    String duplicateSymbolReg = "([。?!?!,]|\\.\\.\\.|……)+";
    input = input.replaceAll(duplicateSymbolReg, "$1");
    System.out.println(input);

    輸出:

    假如生活欺騙了你,相信吧,快樂的日子將會來臨!……

    正則表達式:([。?!?!,]|\\.\\.\\.|……)+,括號中是一個分組:表示一個標點符號,+表示這個分組出現(xiàn)一次或多次,$1分組的內(nèi)容(一個標點符號)。replaceAll 就使用$1去對字符串進行替換了。

    Eg4:

    IP地址排序

    String ip = "192.68.1.254 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30";
    ip = ip.replaceAll("(\\d+)", "00$1");
    System.out.println(ip);
    
    ip = ip.replaceAll("0*(\\d{3})", "$1");
    System.out.println(ip);
    String[] strs = ip.split(" ");
    
    Arrays.sort(strs);
    for (String str : strs) {
        str = str.replaceAll("0*(\\d+)", "$1");
        System.out.println(str);
    }

    輸出:

    00192.0068.001.00254 00102.0049.0023.00013 0010.0010.0010.0010 002.002.002.002 008.00109.0090.0030
    192.068.001.254 102.049.023.013 010.010.010.010 002.002.002.002 008.109.090.030
    2.2.2.2
    8.109.90.30
    10.10.10.10
    102.49.23.13
    192.68.1.254

    • 讓IP地址的每一段都是3位,替換之后有4位的情況

    • 保證IP地址每一段都是3位

    • 排序之

    寫到這里,筆者不禁感嘆,真的很強大!

    4 反向引用

    使用小括號指定一個子表達式分組后,匹配這個子表達式的文本可以在表達式或其它程序中作進一步的處理。默認情況下,每個分組會自動擁有一個組號,規(guī)則是:以分組的左括號為標志,從左向右,第一個分組的組號為1,第二個為2,以此類推。

    Eg:

    /* 這個正則表達式表示 安安靜靜 這樣的疊詞 */
    String regex = "(.)\\1(.)\\2";  
    System.out.println("安安靜靜".matches(regex));   // true
    System.out.println("安靜安靜".matches(regex));   // false

    上面 (.) 表示一個分組,里面 . 表示任意字符,每一個字符都是一個分組,

    \\1表示組1(安)又出現(xiàn)了一次,\\2表示組2(靜)又出現(xiàn)了一次。

    那匹配 安靜安靜,怎么寫正則表達式?根據(jù)上面的例子,將安靜分成一個組,然后這個組又出現(xiàn)了一次就是安靜安靜:

    String regex = "(..)\\1";  
    System.out.println("安靜安靜".matches(regex));   // true
    System.out.println("安安靜靜".matches(regex));   // false

    5 反向引用替換

    Eg1:

    String str = "我我...我我...我要..要要...要要...找找找一個....女女女女...朋朋朋朋朋朋...友友友友友..友.友...友...友友?。。?quot;;
            
    /*將 . 去掉*/
    str = str.replaceAll("\\.+", "");
    System.out.println(str);
    
    str = str.replaceAll("(.)\\1+", "$1");
    System.out.println(str);

    輸出:

    我我我我我要要要要要找找找一個女女女女朋朋朋朋朋朋友友友友友友友友友友?。?!
    我要找一個女朋友!

    (.)表示任意一個字符都會成為一個分組;\\1+ 引用分組(一個字符),表示出現(xiàn)1次或多次這個分組。 $1引用分組(.)將多個重復字符替換成一個字符。

    Eg2:

    替換重復出現(xiàn)的兩位數(shù)之間的內(nèi)容:

    "xx12abdd12345".replaceAll("(\\d{2}).+?\\1", "");  //結(jié)果為 xx345

    是不是覺得很神奇!

    使用replace系列的方法要注意的一個異常: Java replaceAll()方法報錯Illegal group reference

    看完了這篇文章,相信你對“Java正則表達式中如何實現(xiàn)分組和替換”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI