Java 正則表示式全攻略 (一)
Java 正則表示式全攻略 (一)
特此宣告
本文並非全部原創,我只是在前人的基礎上進行鍼對Java的補充和完成。特此向各位前輩致敬。本文來源於以下內容:
什麼是正則表示式
正則表示式(英文:Regular Expression)在電腦科學中,是指一個用來描述或者匹配一系列符合某個句法規則的字串的單個字串。在很多文字編輯器或其他工具裡,正則表示式通常被用來檢索或替換那些符合某個模式的文字內容。許多程式設計語言都支援利用正則表示式進行字串操作。例如,在Perl中就內建了一個功能強大的正則表示式引擎。正則表示式這個概念最初是由Unix中的工具軟體(例如
在現實工作場景中,正則表示式常被用於進行輸入驗證、內容檢索、字串替換等工作。個人認為正則表示式應該算程式設計師的必修課之一,由於它應用的非常之廣泛,而且在各種語言基本上都可以使用,更重要的是在 Linux 或 Unix 系統下能靈活使用正則表示式也可大大提高工作效率,所以學習它是非常保值的。
正則表示式與正則表示式引擎
直觀而言正則表示式是一段負責正則表示式語法的字串,而負責處理這段表示式的程式,就是正則表示式引擎。表示式引擎由語言或環境提供,作為開發者並不直接面對它。我們只需要編寫表示式,然後交給表示式引擎進行處理就可以了。不同語言或環境會以不同方式為我們使用正則表示式提供支援,就拿Java
不同環境的正則表示式寫法與支援或多或少會有一些差別,不過這你可以完全不需要當心,因為這並不影響你使用正則表示式,近代的表示式引擎都非常類似。Perl 5 型別的引擎應該算應用最為廣泛的引擎。如果你想了解各種風格的引擎的語法支援,可以參考《Regexp Syntax Summary》。
表示式與符號
讓我們從一個最簡單的程式(Java)開始說起:
- String string = "gooooooogle";
- String regex = "go*gle";
- System.out.println(string.matches(regex));
這個段程式碼執行的話,將會在控制檯輸出“true”。它是說字串 string 與正則表示式 regex 匹配,換句話說,就是字串 string 符合正則表示式 regex 所描述的模式。在該例子中,最起碼我們可以知道正則表示式的操作物件是字串,而正則表示式也是一個字串。字串又是由字元所構成的,在表示式 go*gle 中 g,o,l,e 是文字字元而 * 是數量限定字元,它代表前面的字元可以不出現,也可以出現一次或者多次。
文字字元
最基本的正則表示式由單個文字元號組成。如 a ,它將匹配字串中第一次出現的字元“a”。如對字串“Jack is a boy”。“J”後的“a”將被匹配。而第二個“a”將不會被匹配。正則表示式也可以匹配第二個“a”,這必須是你告訴正則表示式引擎從第一次匹配的地方開始搜尋。在文字編輯器中,你可以使用“查詢下一個”。如果變成 Java 程式碼的話就是這樣:
- String string = "Jack is a boy";
- // 將字串編譯為正則表示式的物件表示形式
- Pattern pattern = Pattern.compile("a");
- // 建立對字串 string 根據正則表示式 pattern 進行匹配操作的匹配器物件
- Matcher matcher = pattern.matcher(string);
- // 查詢下一個匹配的字串內容,如果找到返回 true,找不到返回 false
- while(matcher.find()) {
- // 輸出捕獲到的匹配內容
- System.out.print(matcher.group() + "\t");
- }
類似的,cat 會匹配“About cats and dogs”中的“cat”。這等於是告訴正則表示式引擎,找到一個c,緊跟一個a,再跟一個t。要注意,正則表示式引擎預設是大小寫敏感的。除非你告訴引擎忽略大小寫,否則 cat 不會匹配“Cat”,就像下面這樣。(除了這種方法,還可以在表示式內宣告什麼內容需要區分大小寫什麼不需要,這在後面會有介紹)
- String string = "About Cats and dogs";
- // 在編譯表示式時使用標記 CASE_INSENSITIVE,使表示式忽略大小寫
- Pattern pattern = Pattern.compile("cat", Pattern.CASE_INSENSITIVE);
- Matcher matcher = pattern.matcher(string);
- while(matcher.find()) {
- System.out.print(matcher.group() + "\t");
- }
特殊字元
對於文字字元,有11個字元被保留作特殊用途。他們是:[ ] \ ^ $ . | ? * + ( )這些特殊字元也被稱作元字元。 如果你想在正則表示式中將這些字元用作文字字元,你需要用反斜槓“\”對其進行換碼 (escape)。例如你想匹配“1+1=2”,正確的表示式為 1\+1=2 。需要注意的是,1+1=2 也是有效的正則表示式。但它不會匹配“1+1=2”,而會匹配“123+111=234”中的“111=2”。因為“+”在這裡表示特殊含義(重複1次到多次)。
不可顯示字元
可以使用特殊字元序列來代表某些不可顯示字元:
\t 代表Tab(0×09)
\r 代表回車符(0x0D)
\n 代表換行符(0x0A)
要注意的是Windows中文字檔案使用“\r\n”來結束一行而Unix使用“\n”。
Java 正則表示式全攻略 (二)
正則表示式引擎的內部工作機制
知道正則表示式引擎是如何工作的,將有助於你很快理解為何某個正則表示式不像你期望的那樣工作,還可以使你清楚如何對錶達式進行效能優化。從最基本的正則表示式引擎實現思路上來分的話,有兩種:確定型有限狀態機(Deterministic Finite-State Automaton)簡稱DFA和不確定型有限狀態機(Nodeterministic Finite-State Automaton)簡稱NFA,也有人稱其為文字導向和正則導向。以下這個網址 http://osteele.com/tools/reanimator/ 以一種非常直觀的方式說明了 DFA 和 NFA 對相同的表示式的不同編譯結果。
由於我們的目的不在於學習狀態機,所以我們忽略這2者的工作原理,直接對比他們的影響。就拿表示式 a|ab|abc|abcd 來對比。 我們可以看到NFA的結果比較複雜,而DFA十分簡潔,這是否又會影響到2者的效能呢?確實如此,DFA的執行速度與表示式無關,它在編譯時的優化已經優於大多數 NFA引擎的複雜優化措施。而NFA的執行速度與表示式有著直接的關係。從匹配結果來看,DFA總是返回最左邊最長的匹配結果,而NFA總是比較猴急,總會匹配第一個找到的結果。根據這一點,我們可以輕易分辨出所使用的引擎是DFA還是NFA,你可以使用表示式 nfa|nfa not 對字串”nfa not”進行測試,如果匹配結果是 nfa ,那該引擎是NFA的,而Java就是屬於NFA的。最後一點就是,NFA能提供的功能比DFA更多,例如:捕獲由括號內的子表示式匹配的文字、環視,以及其他複雜的零長度確認、“惰性”量詞等。而我們講的是Java的正則表示式,那當然也就是在說NFA啦,而NFA由於功能比較多用起來比較方便,因此比DFA要流行些。
正則導向的引擎總是返回最左邊的匹配
這是需要你理解的很重要的一點:即使以後有可能發現一個“更好”的匹配,正則導向的引擎也總是返回最左邊的匹配。 當把 cat 應用到“He captured a catfish for his cat”,引擎先比較 c 和“H”,結果失敗了。於是引擎再比較 c 和“e”,也失敗了。直到第四個字元,c 匹配了“c”。a 匹配了第五個字元。到第六個字元 t 沒能匹配“p”,也失敗了。引擎再繼續從第五個字元重新檢查匹配性。直到第十五個字元開始,cat 匹配上了“catfish”中的“cat”,正則表示式引擎急切的返回第一個匹配的結果,而不會再繼續查詢是否有其他更好的匹配。
字符集
字符集是由一對方括號“[]”括起來的字元集合。使用字符集,你可以告訴正則表示式引擎僅僅匹配多個字元中的一個。如果你想匹配一個“a”或一個“e”,使用 [ae]。你可以使用 gr[ae]y 匹配gray或grey。這在你不確定你要搜尋的字元是採用美國英語還是英國英語時特別有用。相反,gr[ae]y 將不會匹配graay或graey。字符集中的字元順序並沒有什麼關係,結果都是相同的。
你可以使用連字元“-”定義一個字元範圍作為字符集。[0-9] 匹配0到9之間的單個數字。你可以使用不止一個範圍。[0-9a-fA-F] 匹配單個的十六進位制數字,並且大小寫不敏感。你也可以結合範圍定義與單個字元定義。[0-9a-fxA-FX] 匹配一個十六進位制數字或字母X。再次強調一下,字元和範圍定義的先後順序對結果沒有影響。
取反字符集
在左方括號“[”後面緊跟一個尖括號“^”,將會對字符集取反。結果是字符集將匹配任何不在方括號中的字元。不像“.”,取反字符集是可以匹配回車換行符的。
需要記住的很重要的一點是,取反字符集必須要匹配一個字元。q[^u] 並不意味著:匹配一個q,後面沒有u跟著。它意味著:匹配一個q,後面跟著一個不是u的字元。所以它不會匹配“Iraq”中的q,而會匹配“Iraq is a country”中的q和一個空格符。事實上,空格符是匹配中的一部分,因為它是一個“不是u的字元”。如果你只想匹配一個q,條件是q後面有一個不是u的字元,我們可以用後面將講到的向前檢視來解決。
字符集中的元字元
需要注意的是,在字符集中只有4個 字元具有特殊含義。它們是:“] \ ^ -”。“]”代表字符集定義的結束;“\”代表轉義;“^”代表取反;“-”代表範圍定義。其他常見的元字元在字符集定義內部都是正常字元,不需要轉義。例如,要搜尋星號*或加號+,你可以用 [+*] 。當然,如果你對那些通常的元字元進行轉義,你的正則表示式一樣會工作得很好,但是這會降低可讀性。
在字符集定義中為了將反斜槓“\”作為一個文字字元而非特殊含義的字元,你需要用另一個反斜槓對它進行轉義。[\\x] 將會匹配一個反斜槓和一個X。“]^-”都可以用反斜槓進行轉義,或者將他們放在一個不可能使用到他們特殊含義的位置。我們推薦後者,因為這樣可以增加可讀性。比如對於字元“^”,將它放在除了左括號“[”後面的位置,使用的都是文字字元含義而非取反含義。如 [x^] 會匹配一個x或^。[]x] 會匹配一個“]”或“x”。[-x] 或 [x-] 都會匹配一個“-”或“x”。
字符集的簡寫
因為一些字符集非常常用,所以有一些簡寫方式。
. |
任何字元(與行結束符可能匹配也可能不匹配) |
\d |
數字:[0-9] |
\D |
非數字:[^0-9] |
\s |
空白字元:[\t\n\x0b\f\r] |
\S |
非空白字元:[^\s] |
\w |
單詞字元:[a-zA-Z_0-9] |
\W |
非單詞字元:[^\w] |
字符集的重複
如果你用“?*+”操作符來重複一個字符集,你將會重複整個字符集。而不僅是它匹配的那個字元。正則表示式 [0-9]+ 會匹配837以及222。如果你僅僅想重複被匹配的那個字元,可以用向後引用達到目的。我們以後將講到向後引用。
* |
重複零次或更多次 |
+ |
重複一次或更多次 |
? |
重複零次或一次 |
{n} |
重複n次 |
{n,} |
重複n次到更多次 |
{n,m} |
重複n到m次 |
結合前面的知識,我們就可以寫出以下這類常用的表示式:
- // 判斷字串是否一個合法的16進位制
- String regex = "[-+]?0[xX]?[0-9a-fA-F]+";
- System.out.println("0xFF".matches(regex)); // true
- System.out.println("-0Xff".matches(regex)); // true
- System.out.println("ff".matches(regex)); // false
- System.out.println("0x1H".matches(regex)); // false
- // 簡單地判斷一個字串是否合法的身份證號碼
- regex = "\\d{15}|\\d{18}";
- System.out.println("440104700101001".matches(regex)); // ture;
- System.out.println("44010700101001".matches(regex)); // false;
- System.out.println("440104197001010015".matches(regex)); // ture;
- System.out.println("4401041970010100015".matches(regex));// false;
Java 正則表示式全攻略 (三)
數量詞/限定符
從前面的例子中,我們可以瞭解到數量詞,是用來指定正則表示式的一個給定字符集必須要出現多少次才能滿足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6種。每種都有Greedy、Reluctant、Possessive三種匹配方式,Greedy是預設的匹配方式。*、+和?限定符都是貪婪的,因為它們會盡可能多的匹配文字,只有在它們的後面加上一個?就可以實現懶惰或最小匹配。不同的匹配方式,對正則表示式的執行方式和效能影響都是十分重要的。
Greedy 貪婪
Greedy量詞被看作“貪婪”,因為它們在試圖搜尋第一個匹配之前讀完(或者說吃掉)整個輸入字串。如果第一個匹配嘗試(整個輸入字串)失敗,匹配器就會在輸入字串中後退一個字元並且再次嘗試,重複這個過程,直到找到匹配或者沒有更多剩下的字元可以後退為止。以下面程式碼為例(它的輸出結果為“xfooxxxxxxfoo”)。
- Pattern p = Pattern.compile(".*foo");
- Matcher m = p.matcher("xfooxxxxxxfoo");
- while (m.find()) {
- System.out.print(m.group() + "\t");
- }
假設你想用一個正則表示式匹配一個HTML標籤。你知道輸入將會是一個有效的HTML檔案,因此正則表示式不需要排除那些無效的標籤。所以如果是在兩個尖括號之間的內容,就應該是一個HTML標籤。 許多正則表示式的新手會首先想到用正則表示式 <.+> ,他們會很驚訝的發現,對於測試字串,“This is a <EM>first</EM> test”,你可能期望會返回<EM>,然後繼續進行匹配的時候,返回</EM>。 但事實是不會。正則表示式將會匹配“<EM>first</EM>”。很顯然這不是我們想要的結果。原因在於Greedy(貪婪)是預設的配置方式。也就是說,“+”會導致正則表示式引擎試圖儘可能的重複前導字元。只有當這種重複會引起整個正則表示式匹配失敗的情況下,引擎會進行回溯。也就是說,它會放棄最後一次的“重複”,然後處理正則表示式餘下的部分。
相關推薦
Java 正則表示式全攻略 (一)
Java 正則表示式全攻略 (一) 特此宣告 本文並非全部原創,我只是在前人的基礎上進行鍼對Java的補充和完成。特此向各位前輩致敬。本文來源於以下內容: 什麼是正則表示式 正則表示式(英文:Regular E
Java正則表示式過濾、替換,將一段文字中的英語單詞分別提取出,並統計詞頻,按詞頻排序。
最近在學習自然語言處理,在建立基礎標籤庫時,遇到一個需要提取語料中的英文單詞的工作,做好了現在來和大家分享下。 實現效果:讀取檔案內容,把其中的英文單詞提取出,並統計詞頻。提取時,原本不是連在一起的單詞可以分開獨立提取,例如:我的PPT和WORD,可以提取出PPT,WORD兩個單詞。 基本思
Java陣列全攻略(一)
陣列 是一個容器 儲存資料 儲存相同資料型別的資料 陣列的寫法 資料型別[] 陣列名 = 初值; 其中資料型別代表陣列中盛放資料的 資料型別 int[] array =
Java正則表示式初探(一)
好多同學們總是聽別人說起正則表示式這個東西,也有很多用接觸到實際使用過。但是相信有很大一部分人是在用的時候採取網站上搜索,拿一個一知半解的別人寫的例子過來,簡單測試下功能可用,就OK了。正則表示式那晦澀難懂的語法讓很多人望而卻步。但是讀了本文,我相信很多人對於正則表示式就算是基本入門了。最快捷的精通的辦法,還
java正則表示式學習筆記
本人在開發中使用正則表達的場景並不多,偶爾用一下,學習一波,時間久了就又忘記了,放到部落格中,說不定什麼時候就用到了。 一.正則表示式的語法 這個語法表來自:http://www.runoob.com/java/java-regular-expressions.html
JAVA正則表示式區分IPv4和IPv6地址
PS*程式碼直接見第二部分: 一、進入正題前先說說JAVA正則表示式相關概念: 1、常用字元類: [abc] == a||b||c [a-zA-Z] == 所有大小寫字母中的任意一個 [0-9A-Za-z] == 任意
java正則表示式去除html中所有的標籤和特殊HTML字元
關於java正則表示式去除html中所有的標籤和特殊HTML字元,結合我所做的專案總結的經驗: 總共分為三種:第一種適用於適用短的文章,將文章用正則表示式的方式拼接到程式碼中,有些繁瑣,其實不太實用。第二種就是直接將文件引入,進行更改,但是有一個小缺點,就是文件中的格式可能是utf-8格式的
Java正則表示式實現港、澳、臺身份證驗證
最近由於業務的要求,需要進行港、澳、臺人員身份證驗證,現在直接上程式碼,經供參考學習,也為自己積累一些工具類: package com.qiu.validate; public class regexValidateCard { public String validateIdCard10(String id
JAVA正則表示式初探
今天有一個需求 ,提上來類似 123#223#2 這樣的字串,要把裡面的數字匹配出來,就看了一下java的正則 以前一直用python處理正則,相比之下java的正則功能是弱了一點 public static void main(String[] args) { Pat
20180716-Java正則表示式
import java.util.regex.Matcher;import java.util.regex.Pattern; public class RegexMatches{ public static void main(String[] args){ //按指定模式在字串查詢 String line
java正則表示式的使用:6位數字
public static void main(String[] args) { Pattern pattern = Pattern.compile("\\d{6}"); boolean matches = pattern.matcher("a21109").matches(); S
Java——正則表示式
正則表示式定義了字串的模式。 正則表示式可以用來搜尋、編輯或處理文字。 正則表示式並不僅限於某一種語言,但是在每種語言中有細微的差別。 java.util.regex 包主要包括以下三個類: Pattern 類: pattern 物件是一個正則表示式的編譯表示。Pattern
EL表示式全攻略
EL表示式 1、EL簡介 1)語法結構 ${expression} 2)[]與.運算子
java正則表示式校驗
Pattern pattern = Pattern.compile("\\d+\\.\\d+$|-\\d+\\.\\d+$");//判斷是否為小數 Pattern pattern1 = Pattern.compile("^\\d+$|-\\d+$"); if (pattern.match
Java正則表示式匹配日期及基本使用
廢話不多說,直接上程式碼: package top.yangxianyang.test; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.Test; public
Java正則表示式學習與記錄
轉載自:http://www.runoob.com/java/java-regular-expressions.html 正則表示式定義了字串的模式,用於搜尋、編輯或處理文字。 1、正則表示式中字元意義: \ 將下一字元標
Java正則表示式應用小結
案例: Pattern p = Pattern.compile("a*b"); // 生成Pattern例項(設定匹配模式【規則】)- 靜態方法 Matcher m = p.matcher("aaaaab"); // 生成Match例項(
java 正則表示式 複習
正則表示式在日常開發中會經常的接觸到,學會了正則可以更有效的對字元進行驗證、拆分、替換、判斷字串是否合法等操作。。。 常用語法: 字元的取值範圍 1.[abc] : 表示可能是a,可能是b,也可能是c。 2.[^abc]: 表示不是a,b,c中的任意一個 3.[a-zA-Z]: 表示
正則表示式學習筆記(一)
開始和結束 ^ $ '^' 表示開始 例如:'^the' 表示以 the 開頭的詞 '$' 表示結束 例如:'the$' 表示以 the 結尾的詞 'the' 表示 包含 the 的詞
java正則表示式匹配所有相匹配的內容
java使用正則表示式匹配所有內容 ---- ** 構建正則表示式 String patternCode="baseid=\\w+"; String patternTitle="title=\\\"[\\u4e00-\\u9fa5]*·?[\\u4e00-\\u9fa5]*\\(?[