前情引入
-
什麼是正規表達式?
正規表達式,又稱規則表達式。(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),計算機科學的一個概念。正規表達式通常被用來檢索、替換那些符合某個模式(規則)的文本—— 來自百度的解釋。
說得簡單一點,正規表達式就是一套模式,一套對應規則。就像我們使用的字元集一樣,有對應關系。字元集是用來與我們使用的語言文字對應的,正規表達式則是用來與字元對應的。
比如在ASCII碼中,97對應的是小寫字母"a",在正規表達式中,"\w" 就對應任何一個單詞字元(數字、字母和下劃線)
-
正規表達式有什麼用?
有了這套對應關系之後,我們就可以很友善的對字元串(或稱文本)進行各種操作,檢索、替換等等。正規表達式被廣泛的應用在計算機的各個領域,主流的開發語言(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"));
暫時就說這麼多,最基本的内容。
熱身練習
- 比對非負整數
- 比對正整數
- 比對整數
習題是我從别處搬來的,想練習可以去這裡:https://blog.csdn.net/qian_youyou/article/details/79121916
他給的答案中,每個表達式最前面都是"^",最後面都是"$",大家如果不知道的這個東西話,可以暫時将其忽略。