天天看點

正規表達式入門——java語言

前情引入

  1. 什麼是正規表達式?

    正規表達式,又稱規則表達式。(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),計算機科學的一個概念。正規表達式通常被用來檢索、替換那些符合某個模式(規則)的文本—— 來自百度的解釋。

    說得簡單一點,正規表達式就是一套模式,一套對應規則。就像我們使用的字元集一樣,有對應關系。字元集是用來與我們使用的語言文字對應的,正規表達式則是用來與字元對應的。

    比如在ASCII碼中,97對應的是小寫字母"a",在正規表達式中,"\w" 就對應任何一個單詞字元(數字、字母和下劃線)

  2. 正規表達式有什麼用?

    有了這套對應關系之後,我們就可以很友善的對字元串(或稱文本)進行各種操作,檢索、替換等等。正規表達式被廣泛的應用在計算機的各個領域,主流的開發語言(java、c、python、c++、js……)和數以億萬記的軟體中,都可以看到正規表達式的身影。

正規表達式和java語言本身沒有關系

入門初體驗

在java中,java.util.regex包下的類都是和正則相關的類,我們來簡單的體驗一下

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTest02
{
    public static void main(String[] args)
    {
        regularTest();
    }

    public static void regularTest()
    {
        Pattern pattern = Pattern.compile("\\w");//被編譯後的表達式對象
        Matcher matcher = pattern.matcher("abc123##");//需要比對的對象(就是需要操作的字元串)
        boolean isMatch = matcher.matches();//matches這個方法是嘗試比對整個字元串
        
        //下面這種寫法和上面的那種寫法效果是一樣的,隻是上面的寫法,可以将表達式對象和需要比對的對象複用
        boolean isMatch1 = Pattern.matches("\\w", "a");//也是嘗試比對整個字元串
        System.out.println(isMatch);
        System.out.println(isMatch1);
    }
}

           

13行:表示擷取一個被編譯之後的正規表達式對象

14行:表示需要比對的對象,也就是要對那個字元串進行操作

16行:matches方法是嘗試比對整個目标字元串,如果比對成功,則傳回true,否則傳回false

如果是初學者,可能對于上面的内容不是很了解,但是java這種面向對象的語言,有這個好處:我們不需要知道為什麼要這麼做,而隻需要知道我們如果要完成一個功能,需要怎麼做(對于初學者而言)

我們需要怎麼做?讀文檔、查資料,看哪個類、哪個方法能幫我們完成功能。現在我已經告訴你了,你隻需要像上面這麼做,就能達到我們想要的目的,是以,你照着上面這麼寫就好了。

需要注意一點的是,在java中,反斜杠 “\” 具有特殊含義:轉義,是以想要表示一個普通的反斜杠,一個反斜杠是不行的,要用兩個反斜杠,前面那個反斜杠的作用就是轉義,将後面這個反斜杠轉義為一個普通的反斜杠。如果裡是在了解不了的話,你就記死結論:java中兩個反斜杠表示一個普通的反斜杠。

順便提一嘴,在正規表達式中,反斜杠一樣具有特殊含義,是以要在java中使用正規表達式表示一個普通的反斜杠,需要用四個反斜杠表示一個普通的反斜杠,後面會講到。

現在我們來推測一下程式的運作結果,前面我已經說了,"\w" 就對代表何一個單詞字元(數字、字母和下劃線),那麼控制台應該列印true還是false呢?如果将14行中的 “abc123##” 換成 “abc” 呢?如果換成 “a” 或其他的單詞字元呢?可以自己去測試一下,再來看結果分析。

前兩個都是false,因為 “\w” 隻代表一個單詞字元,而前兩個目标字元串裡都不止一個字元。如果換成 “a” 或其它的單詞字元,肯定就是true了。

正式認識

前面我們簡單的使用了正規表達式,下面我們來系統的認識一下正則的文法規則。簡單的正規表達式大緻由兩部分組成:比對内容和比對次數,複雜的還有其它一些東西。

上面我們說的,"\w" 代表任意一個單詞字元,但是如果我們要代表兩個單詞字元呢?三個或者更多個呢?

三個常用的表示次數的符号:
  • ? :表示比對前面的字元零次或一次
  • +:表示比對前面的字元一次或多次
  • * :表示比對前面的字元任意次(零次或多次)

代碼測試,還是上面那幾行代碼

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTest02
{
    public static void main(String[] args)
    {
        regularTest();
    }

    public static void regularTest()
    {
        System.out.println(Pattern.matches("\\w?","zbc"));
        System.out.println(Pattern.matches("\\w+","zbc"));
        System.out.println(Pattern.matches("\\w*","zbc"));
		System.out.println(Pattern.matches("\\w?",""));
		System.out.println(Pattern.matches("\\w+",""));
		System.out.println(Pattern.matches("\\w*",""));
    }
}

           

上面程式的列印結果是什麼呢?

13行:false,因為 “?” 隻代表前面的字元零次或一次,它的前一個字元是什麼? 是 “\w” ,表示任意一個單詞字元。是以整體意思表示比對任意一個單詞字元零次或一次。但是下面的需要比對的字元串對象中,卻有三個字元,是以肯定不比對。

14行:true,因為 “+” 代表比對前面的字元一次或多次,前一個字元是任意一個單詞字元,是以,隻要不為空,多少個單詞字元拼在一起都可以比對成功,當然是true了。

後面的嘗試自己分析一下,另外,自己還可以多試一些邊,加深影響。注意考慮三個表示次數的差別去改。

其它表示次數的形式
  • {n,m}:比對前面的字元n次到m次
  • {n,}:比對前面的字元n次到無限次
  • {n}:比對前面的字元n次

這三個,可以明确的表示一個次數邊界,感覺上會用得多一些。但實際上用得并不多(就我個人經驗而言),因為在對一些文本或字元串進行操作的時候,我們往往是不清楚個數的。

我就不測試了,大家感興趣的話可以去測試一下,将之前的 “?”、"+"、"*" 改成上面的就可以了。

了解了表示比對次數的文法之後,我們來認識一下表示比對其它内容的規則

常用的比對内容規則
  • \w:表示比對任意一個單詞字元(單詞、字元和下劃線)
  • \W:表示比對任意一個非單詞字元
  • \d:表示比對任意一個十進制數字
  • \D:表示比對任意一個非十進制數字
  • \s:表示比對任意一個空白字元(包括空格、制表符、換頁符等等)
  • \S:表示比對任意一個非空白字元,也就是可見字元
  • . : (英文狀态下的點.) 表示比對一個除換行符之外的其它任意字元

這三對都是相對應的,記住一個,另一個也就知道了。當然,還有很多表示表示比對其它的規則,大家先熟悉和掌握這些基本的、常用的,再往了解其它的,有個循序漸進的過程。

代碼測試,知道了這些比對内容的規則,再結合前面的比對次數,我們就可以換更多的花樣來測試了。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTest02
{
    public static void main(String[] args)
    {
        regularTest();
    }

    public static void regularTest()
    {
        System.out.println(Pattern.matches("\\d?","zbc"));
        System.out.println(Pattern.matches("\\d+","zbc"));
        System.out.println(Pattern.matches("\\D*","zbc"));
		System.out.println(Pattern.matches("\\s?",""));
		System.out.println(Pattern.matches("\\S+",""));
		System.out.println(Pattern.matches("\\S*",""));
		System.out.println(Pattern.matches(".","a"));
		System.out.println(Pattern.matches(".?","abc"));
		System.out.println(Pattern.matches(".+","abc"));
    }
}
           

結果我就不帶領大家分析了,自己慢慢分析,應該是可以得出結果的。如果分析錯了,結合控制台的列印,應該也能很快反應過來的。

下面,我們來認識一下正規表達式中另一類神奇的存在:元字元。其實我們在之前已經用過了, 隻是我們不知道它是元字元。例如:" "、 “{}”、 “*” ……這些都是元字元。什麼?我之前用得好好的,現在你告訴我它是元字元,我不能接受!!

元字元是什麼意思呢?就是具有特殊含義的字元。說到特殊含義,那什麼是沒有特殊含義的字元呢?說到現在,我們一直都在說比對任意xxxx,那我就想比對一個 “w” ,一個 “s” 呢?這咋辦。

其實很簡單

System.out.println(Pattern.matches("w","w"));
		System.out.println(Pattern.matches("w?","www"));
		System.out.println(Pattern.matches("s+","sss"));
		System.out.println(Pattern.matches("s*","ass"));
           

如果是普通字元,單寫一個,就代表比對一個該字元。

現在我們再回頭來看元字元,是不是具有特殊含義?是的吧,

常用的元字元
  • . : 表示比對一個除換行符之外的其它任意字元
  • ? :表示比對前面的字元零次或一次
  • + :表示比對前面的字元一次或多次
  • * :表示比對前面的字元任意次(零次或多次)
  • \:加在元字元前面可以消除其特殊功能,加在一些普通字元前面可以使其具有特殊含義
  • {}:成對出現,表示比對中括号中指定的次數
  • []:成對出現,表示一個字元集,或者是一個範圍,比對其中一個。有些元字元在其中沒有特殊含義
  • ():表示一個整體,也可以表示分組(子組),結合比對對象進行使用
  • ^:脫字元,表示除xxx之外。還表示從字元串的頭開始比對
  • |:對兩個正規表達式進行或操作,這個符号優先級别很低

有一些是老熟人了吧,我就不說了哦。

1. \

“\” 好像挺有意思的,加在元字元前面可以消除其特殊功能,加在一些普通字元前面可以使其具有特殊含義,是什麼意思呢?我們都知道了,"+" 表示比對前一個字元一次或多次,那我就想比對 “+” 本身怎麼辦?

//這樣寫肯定是不行的
		System.out.println(Pattern.matches("+","++"));
           

那怎麼辦呢?這時候就要用到 “\” 了,讓 “\” 把 “+” 這個元字元的特殊含義給消除了,是以就這麼寫:

//注意要用雙反斜杠,前面已經解釋過了
		System.out.println(Pattern.matches("\\+","++"));
		System.out.println(Pattern.matches("\\+?","++"));
           

要比對其它的元字元也是類似,但是有個問題是:我就想比對一個反斜杠,這咋辦?

其實思路還是一樣的,反斜杠不是具有特殊含義嗎?那我就再用一個反斜杠,來消除它的特殊含義,隻不過在java中,反斜杠也具有特殊含義,是以我們得用兩個反斜杠表示字元串中一個普通的反斜杠,是以我們得用四個反斜杠來表示正規表達式中一個普通的反斜杠,每兩個反斜杠,表示正規表達式中一個具有特殊含義的反斜杠,然後再用反斜杠消除特殊後面一個反斜杠的特殊含義。

System.out.println(Pattern.matches("\\\\","\\"));
		System.out.println(Pattern.matches("\\\\?","\\\\"));
		System.out.println(Pattern.matches("\\\\*","\\\\\\"));
           

“\” 的另外一個作用就是,将一些普通字元變得有特殊含義,其實我們早已經用過了,就是在比對内容那裡,“w”、“W”、“s”、“S”、“d”、“D”……這些原本都是普通的字元,隻代表比對它本身,但是在它前面加一個反斜杠之後,就賦予了它特殊的含義。

2. []

“[]” 表示一個字元集,比對其中一個。例如:"[123]" 表示比對 “1” 或 “2” 或 “3”。

當連字元( “-” )在字元集中出現在兩個有順序的字元之間時,表示比對這個範圍之内的任意一個,比如:"[1-3]" 的效果就和 “[123]” 一樣,都表示比對 “1” 或 “2” 或 “3”。"[a-c]"就表示比對 “a” 或 “b” 或 “c”

System.out.println(Pattern.matches("[1-3]","3"));
		System.out.println(Pattern.matches("[a-c]","a"));
		System.out.println(Pattern.matches("[a-c]","b"));
		System.out.println(Pattern.matches("[a-c]","c"));
           

注意:當連字元不是出現在兩個 具 有 順 序 關 系 \color{red}{具有順序關系} 具有順序關系的字元之間時,就表示比對連字元本身。

比如:"[-123]"、"[123-]" 和 “[1-3-]” 都表示比對 “-” 或 “1” 或 “2” 或"3"

System.out.println(Pattern.matches("[-123]","-"));//true
		System.out.println(Pattern.matches("[123-]","-"));//true
		System.out.println(Pattern.matches("[1-3-]","-"));//true
           

在比如"[1-z]"就表示比對 “1” 或 “-” 或 “z”,如果是 “[3-1]” 這種逆序的寫法,在java中會報錯,編譯不通過,不知道其它情況下是怎樣的。

還有就是有些元字元在 “[ ]” 中沒有特殊含義!!

3. ()

“()” 表示一個整體,比如說,我要比對 “110” 一次或多次,就可以用 “()” 将 “110” 表示為一個整體,然後後面再規定次數。

System.out.println(Pattern.matches("(110)+","110"));
        System.out.println(Pattern.matches("(110)+","110110"));
        System.out.println(Pattern.matches("(110)+","110011"));
           

關于表示子組後面再說。

4. ^

“^” 在字元集 “[]” 中,如果出現在第一個,是脫字元,表示除去什麼什麼。

比如 “[^123]” 表示比對任意一個 “1”、“2”、“3” 之外的字元。 " [^a-d]" 表示比對任意一個除"a"、“b”、“c”、“d” 之外的字元。

System.out.println(Pattern.matches("[^123]","3"));
        System.out.println(Pattern.matches("[^123]","5"));
        System.out.println(Pattern.matches("[^a-d]","d"));
        System.out.println(Pattern.matches("[^a-d]","z"));
           

如果 “^” 在 “[]” 中,不是出現在第一個的,就表示一個普通的 “^” 字元。比如 “[12^3]” 就表示比對 “1” 或 “2” 或 “^” 或 “3”。

如果"^“不是出現在字元集”[ ]"中,是表示從頭開始比對的意思,但是我不知道用java怎麼示範。

5. |

“|” 表示對兩個正規表達式進行或操作,這個符号優先級别很低,比如:“ab|cd” 不是表示比對 “abd” 或 “acd” ,而是表示比對 “ab” 或 "cd"

System.out.println(Pattern.matches("ab|cd","abd"));
        System.out.println(Pattern.matches("ab|cd","acd"));
        System.out.println(Pattern.matches("ab|cd","ab"));
        System.out.println(Pattern.matches("ab|cd","cd"));
           

暫時就說這麼多,最基本的内容。

熱身練習

  1. 比對非負整數
  2. 比對正整數
  3. 比對整數

習題是我從别處搬來的,想練習可以去這裡:https://blog.csdn.net/qian_youyou/article/details/79121916

他給的答案中,每個表達式最前面都是"^",最後面都是"$",大家如果不知道的這個東西話,可以暫時将其忽略。

如果哪裡寫得有問題,還請指出。

繼續閱讀