天天看點

java 正規表達式學習筆記

概述

正規表達式能夠解決各種字元串處理的問題:

比對

選擇

編輯

以及

驗證

。正規表達式是一種強大靈活的文本處理工具,在很多語言中都支援正規表達式。本文主要介紹的是java的正規表達式的使用。

基礎

在正規表達式中我們要表示一個數字,可以用\d表示。但是java對\的處理和其他語言不同。其他語言中\表示 要在正規表達式中插入一個字面上的反斜線,沒有特殊含義,而在java中,\表示 我要在正規表達式中插入一個反斜線,其後面的字元具有特殊的含義。 是以java中,你要表示數字就是\d,要表示一個字面上的反斜線就是\\。當然換行和制表符除外:\n\t。

以下是是特殊字元代表的含義。

java 正規表達式學習筆記
java 正規表達式學習筆記
java 正規表達式學習筆記

例子:

public class Test {
    public static void main(String[] args) {
        //判斷數字
        System.out.println("asda".matches("\\d+"));

        //?是存在一個或零個之前的符号
        System.out.println("2321".matches("-?\\d+"));
        System.out.println("+2321".matches("-?\\d+"));

    }
}
/*
false
true
false
 */
           

Pattern 類

上面的例子是String自帶的正則驗證,還有一個更強大的替換工具,而且具有更佳的性能。

那就是

java.util.regex.Pattern

java.util.regex.Matcher

,String内部也是使用了這個類來實作

第一步,通過正規表達式建立模式對象 Pattern。

第二步,通過模式對象 Pattern,根據指定字元串建立比對對象 Matcher。

第三步,通過比對對象 Matcher,根據正規表達式操作字元串。

例子1:

比對模式

有三種比對方式,叫做貪婪型,非貪婪型,占有型。對于這三種比對模式也有叫:

“最大比對Greedy”``“最小比對Reluctant”``“完全比對Possessive”

貪婪 勉強 侵占 含義
X? X?? X?+ 比對 X 零次或一次
X* X*? X*+ 比對 X 零次或多次
X+ X+? X++ 比對 X 一次或多次
X{n} X{n}? X{n}+ 比對 X n 次(這個應該不存在這幾種模式,就是固定比對n個)
X{n,} X{n,}? X{n,}+ 比對 X 至少 n 次
X{n,m} X{n,m}? X{n,m}+ 比對 X 至少 n 次,但不多于 m 次

從上表可以看出貪婪型是預設的比對方式,勉強型是在貪婪型後面加上?,占有型是在貪婪型後面加上+。

Greediness(貪婪型):也叫最大比對

例如你要用 “<.+>”去比對“aaavaabb”,也許你所期待的結果是想比對 “”,但是實際結果卻會比對到“aava”。這是為什麼呢?下面我們跟蹤下最大比對的匹 配過程。

①“<”比對字元串的“<”。

②“.+”比對字元串的“tr>aavaab”,在進行最大比對時,它把兩個 “>”都比對了,它比對了所有字元,直到文本的最後字元“b”③這時,發現不能成功比對“>”,開始按原路回退,用“a”與“>”匹 配,直到“ab”前面的“>”比對成功。

Reluctant(Laziness)(勉強型):最小比對

最小比對意味者,.+? 比對一個字元後,馬上試一試>的比對可能,失敗了,則.+?再比對一個字元,再馬上試一試>的比對可能。JDK文檔中Greedy 和Reluctant,它是以eat一口來隐喻的,是以翻譯成貪吃和(勉強的)厭食最貼切了。不過我喜歡最大比對、最小比對的說法。

Possessive(占有型):完全比對

與最大比對不同,還有一種比對形式:X?+、X*+、X++、X{n}+、X{n,}+、X{n,m}+等,成為完全比對。它和最大比對一樣,一直比對所有的字元,直到文本的最後,但它不原路傳回。

這個不大好了解,有一個例子摘自http://bbs.csdn.net/topics/390269371比較形象:

字元串為aaa,正規表達式為[a]+,這是一個貪婪的比對,直接傳回aaa。但是如果是占有型的,正則為[a]+a,傳回結果是false,是空。

如果a*a,是比對優先的,也就是說先比對,如果正則的後續部分不能再比對,就回溯,在這個例子中,比對字元串aaa的時候,首先a比對到最後一個,然後發現正則後面還有一個a沒法比對,就會将a*回溯到字元串的中間一個a,這時候正則中的最後一個a與字元串的最後一個a正好比對,比對結束。

如果正則是a*+a,+是占有優先,也就是說+前面的字元會盡可能比對,比對了的就不會再回溯,不會讓回去了,即所謂占有。如果字元串是aaa,那麼這個例子中比對過程就是a*+比對了字元串的三個a,正則中的最後一個a不會再被比對,因為a*+不會回溯。

來源:http://blog.csdn.net/xh16319/article/details/27498287

例子:(特殊提醒在正規表達式中[,左中括号是特殊字元,是以要加\,]右中括号不是特殊字元是以不用加\):

String str = "This is [google][1],this is [apple][2],and this is [ms][3]";
//貪婪比對
Pattern pattern1 = Pattern.compile("\\[.*]");
Matcher matcher1 = pattern1.matcher(str);
while (matcher1.find()) {
    System.out.println(matcher1.group());
}
System.out.println("----------");
//非貪婪比對
Pattern pattern2 = Pattern.compile("\\[.*?]");
Matcher matcher2 = pattern2.matcher(str);
while (matcher2.find()) {
    System.out.println(matcher2.group());
}
System.out.println("----------");
//獨占比對
String str1 = "[google1]";
//下面兩種會有不同的而結果,因為.*是占有的他先會把所有符合的比對光,是以第一個比對時str1中的]會被比對在.*中,當比對後面]的時候沒有字元給他比對了。
//Pattern pattern3 = Pattern.compile("\\[.*+]");
Pattern pattern3 = Pattern.compile("\\[.*+");
Matcher matcher3 = pattern3.matcher(str1);
while (matcher3.find()) {
    System.out.println(matcher3.group());
}
           

由于三種方式比對的規則不同,有着不同的計算量,是以引發的問題:一個有正規表達式引發的血案 http://zhuanlan.51cto.com/art/201708/549051.htm

分組和标簽

正規表達式還有一個分組的功能。(a)((b)(c))當用這個當做正則表達是比對的時候,每個括号都可以當做一個組。組号為從左到右

左括号出現的個數,0代表全部。group()或者group(0)代表全部,group(1)為(a), group(2)為((b)(c)),group(3)為(b),group(3)為(c)。

比如:

public static void test3() {
        String str = "1111bbbbbb    ccccc22222";
        //貪婪比對
        Pattern pattern1 = Pattern.compile("(\\d+)([a-zA-Z]+)(\\s+)([a-zA-Z]+)(\\d+)");
        Matcher matcher1 = pattern1.matcher(str);
        while (matcher1.find()) {
            System.out.println("group():" + matcher1.group());
            System.out.println("group(0):" + matcher1.group());
            System.out.println("group(1):" + matcher1.group());
            System.out.println("group(2):" + matcher1.group());
            System.out.println("group(3):" + matcher1.group());
            System.out.println("group(4):" + matcher1.group());
        }

    }
           
還通過?<name>對組進行命名。
//對組1群組2進行命名
public static void test4() {
        String str = "1111bbbbbb    ccccc22222";
        //貪婪比對
        Pattern pattern1 = Pattern.compile("(?<name1>\\d+)(?<name2>[a-zA-Z]+)(\\s+)([a-zA-Z]+)(\\d+)");
        Matcher matcher1 = pattern1.matcher(str);
        while (matcher1.find()) {
            System.out.println("group():" + matcher1.group("name1"));
            System.out.println("group(0):" + matcher1.group("name2"));
        }

    }
           

線上工具以及常用的正在表達式

參考資料:

  • 《java程式設計思想》
  • 《Java程式設計思想第四版》筆記—13章 字元串