本文是系列博客的第一篇,主要講解和分析正則表達式規則以及JAVA中原生正則表達式引擎的使用。在後續的文章中會涉及基於NFA的正則表達式引擎內部的工做原理,並在此基礎上用1000行左右的JAVA代碼,實現一個支持經常使用功能的正則表達式引擎。它支持貪婪匹配和懶惰匹配;支持零寬度字符(如「\b」, 「\B」);支持經常使用字符集(如「\d」, 「\s」等);支持自定義字符集(「[a-f]」,「[^b-k] 」等);支持全部重複操做(「*」,「+」,「?」,「{n,m}」等);支持通配符(「. 」);支持運算符自己的轉義字符(「\*」,「\.」等);支持命名捕獲組。本系列博客的目的是理解正則表達式的內部工做原理,因此沒有考慮正則表達式引擎的工做效率以及正負斷言和非命名捕獲組等不經常使用的功能,後續的工做將會對其優化並完備其功能。因爲正則表達式引擎的實現須要運用數據結構中的相關內容,閱讀本系列博客前請熟悉棧,隊列,圖以及JAVA中容器等相關知識。html
歡迎探討,若有錯誤敬請指正 java
如需轉載,請註明出處 http://www.cnblogs.com/nullzx/正則表達式
正則表達式的功能就是在文本串中搜索特定模式的字符串。咱們如下面方框中豆瓣電影網頁中給出的信息爲例,咱們想在這些文本中找出全部的日期信息,咱們發現日期信息的字符格式在如下文本串中具備特定的格式,都是xxxx-xx-xx的模式(好比2017-01-27),這裏的x表示一個具體的數字。因此咱們搜索的字符串的格式就是「\d{4}-\d{2}-\d{2}」,在正則表達式中\d表示數字,{n}表示重複n次。網絡
猜火車2 數據結構 猜火車2 / 迷幻列車2(港) / T2:Trainspotting函數 2017-01-27(英國) / 伊萬·麥克格雷格 / 約翰尼·李·米勒 / 羅伯特·卡萊爾 / 艾文·布萊納 / 雪莉·亨德森 / 安傑拉·奈迪亞科娃 / 史蒂文·羅伯特森 / 戈登·肯尼迪 / 西蒙·韋爾 / 詹姆斯·卡沙莫 / 梁佩詩 / 阿塔·雅谷伯 / 埃文·威爾什 /...工具 ...測試 ...優化 ...ui 7.8 (5392人評價) 寶貝老闆 寶貝老闆 / 娃娃老闆 / 波士BB(港) 2017-03-12(邁阿密電影節) / 2017-03-31(美國) / 亞歷克·鮑德溫 / 邁爾斯·克里斯托弗·巴克什 / 吉米·坎摩爾 / 麗莎·庫卓 / 史蒂夫·布西密 / 託比·馬奎爾 / 詹姆斯·麥格拉思 / 康拉德·弗農 / 薇薇安·葉 / 小埃裏克·貝爾 / 大衛·索倫 / 伊迪·米爾曼... 8.3 (184408人評價) 逃出絕命鎮 逃出絕命鎮 / 訪‧嚇(港) 2017-01-23(聖丹斯電影節) / 2017-02-24(美國) / 丹尼爾·卡盧亞 / 艾莉森·威廉姆斯 / 凱瑟琳·基納 / 布萊德利·惠特福德 / 卡賴伯·蘭德里·瓊斯 / 馬庫斯·亨德森 / 貝蒂·加布裏埃爾 / 勒凱斯·斯坦菲爾德 / 斯蒂芬·魯特 / 李雷爾·哈瓦瑞... 7.5 (51576人評價) |
因爲排版的須要,以上文本框中的內容比下圖實際處理數據中的內容爲基礎進行了刪減
咱們經過正則表達式測試工具進行文本串中特定模式串\d{4}-\d{2}-\d{2}匹配,結果以下圖所示
經過獲得的處理結果,咱們搜索到了文本串中全部的日期信息。從這個例子咱們能夠看出正則表達式引擎的主要功能就是在給定的文本串中搜索符合正則表達規則的特定模式的字符串,而這個特定的模式是咱們經過分析文本串中感興趣的信息總結獲得的通常規律。好比要獲得文本中電影的評分,字符串的格式就是「\d.\d」。
除了上述例子外,正則表達式還有不少應用。例如,在註冊用戶時,驗證用戶輸入的郵箱是否合法;在網絡爬蟲技術中,爬取咱們感興趣的相關內容;編譯器設計中,咱們還能夠將正則表達式做爲詞法分析器,等等。使用正則表達式可以使咱們更方便,更加高效的解決字符串模式匹配的相關問題,而沒必要爲每個問題單獨寫一個程序。這裏咱們所說的效率高,是指編寫程序的效率更高,而非程序的運行效率。
咱們的目的是寫一個正則表達式引擎,因此咱們接下來就很是有必要了解一下正則表達式的通常規則。
2.1 自定義字符集
[abc] |
a或b或c |
[^abc] |
除了a,b,c的其它字符 |
[a-zA-Z] |
知足a-z範圍的字符或A-Z範圍的字符 |
例子:下面的正則表達式會匹配兩個字符,第一個爲小寫字母,第二個爲數字,文本串中已捕獲的內容用紅色表示。
正則表達式:「[a-z][0-9]」
文本串內容:「absef809sefdk1dfes12389」
2.2 已定義字符集
. |
能夠匹配非換行符之外的任何字符,可否匹配換行符是可配置的 |
\d |
數字,等價於[0-9] |
\D |
非數字,等價於[^0-9] |
\s |
空白符,等價於[ \t\n\x0B\f\r] |
\S |
非空白符,等價於[^\s] |
\w |
字母、數字或下劃線,等價於[a-zA-Z_0-9] |
\W |
非字母和數字,等價於[^\w] |
例子:下面的正則表達式會匹配以非空白字符開頭和非空白字符結尾,中間是「abc」的字符串,總共須要捕獲5個字符,文本串中已捕獲的內容用紅色表示。
正則表達式:「\Sabc\S」
文本串內容:「abcd abc defabcyjkabc」
2.3 轉義字符(不解釋)
\t |
The tab character ('\u0009') |
\n |
The newline (line feed) character ('\u000A') |
\r |
The carriage-return character ('\u000D') |
\f |
The form-feed character ('\u000C') |
\a |
The alert (bell) character ('\u0007') |
\e |
The escape character ('\u001B') |
\cx |
The control character corresponding to x |
2.4 零寬度邊界字符
零寬度邊界字符,只會匹配一個位置而不會佔有字符
^ |
行開始 |
$ |
行結束 |
\b |
單詞的開始邊界或結束邊界 |
\B |
非單詞的邊界 |
例子:下面的正則表達式會匹配字符串「abc」,而且要求第一個字符‘a’的前面不是字母字符和數字字符,最後一個字符‘c’的後面不是字母字符和數字字符。正則表達式總共須要捕獲3個字符,文本串中已捕獲的內容用紅色表示。
正則表達式:「\babc\b」
文本串內容:「abc dabcd abc abcd -abc」
2.5 貪婪重複模式(儘可能多重複)
X表示一個合法的正則表達式
X? |
X重複一次或0次 |
X* |
X,重複0次或屢次 |
X+ |
X重複至少1次 |
X{n} |
X重複恰好n次 |
X{n,} |
X重複至少n次 |
X{n,m} |
X重複至少n次,最多m次 |
例子:下面的正則表達式會匹配以a開頭和a界結尾的,中間有儘量多的其它字符,且其它字符要求至少有一次,文本串中已捕獲的內容用紅色表示。
正則表達式:「a.+a」
文本串內容:「zxyabcdefasseaa09876」
2.6 懶惰重複模式(儘可能少重複)
X?? |
X重複一次或0次 |
X*? |
X,重複0次或屢次 |
X+? |
X重複至少1次 |
X{n}? |
X重複恰好n次 |
X{n,}? |
X重複至少n次 |
X{n,m}? |
X重複至少n次,最多m次 |
例子:下面的正則表達式會匹配以a開頭和a界結尾的,中間有儘量少的其它任意字符,且其它任意字符要求至少有一次。文本串中已捕獲的內容用紅色表示。
正則表達式:「a.+?a」
文本串內容:「zxyabcdefasseaa09876」
2.7 邏輯運算符
X和Y分別表示兩個正則表達式
XY先知足正則表達式X,而後知足正則表達式Y的正則表達式
X|Y 知足正則表達式X或知足正則表達式Y的正則表達式
注意優先級,X|YZ 等價於 X|(YZ),而(X|Y)Z表示XZ|YZ
正則表達式:「a(b|c)d」
文本串內容:「zxyabcdefacdeaabd876」
2.8 括號
在正則表達式中的做用有兩個,一個和四則運算中的括號相同,用來改變優先級,另外一個用於分組捕獲。分組捕獲又分爲兩種,一種是自定義命名的分組,還有一種是未命名的分組(或者稱爲自動編號分組)。
命名分組的格式爲:(?<name>X),其中X表示一個正則表達式
例子:下面的正則表達式表示已數字開頭,中間是字母,以數字結尾的字符串。名爲letter的捕獲組捕獲符合該正則表達式中間爲字母的部分。文本串中捕獲的內容用紅色表示。
正則表達式:「\d+(?<letter>[a-zA-Z]+)\d+」
文本串內容:「0123ab456def gisd4ZDG6zz」
名爲letter的捕獲組中的內容爲:「0123ab456def gisd4ZDG6zz」
對於未命名分組,每一對括號實際上都是一對分組,正則表達式引擎會在編譯該表達式的時候會從左到右掃描正則表達式,對未命名分組進行編號。遇到的第1個左括號(和相應匹配的右括號)是第1組,遇到的第2個左括號(和相應匹配的右括號)是第2組,……。第0組的內容匹配的是整個正則表達式。實際上組號分配過程是要從左向右掃描兩遍的:第一遍只給未命名組分配,第二遍只給自定義命名組分配,也就是說自定義命名分組也是有編號的,且全部自定義命名組的組號都大於未命名的組號。
2.9 特殊字符的匹配
對於一些在正則表達式中具備特殊含義的特殊字符,好比「{」,「*」「\」等等,若是咱們想在文本串中捕獲它們,就只能經過轉義的方式。好比咱們想匹配文本串中以「{」花括號開頭,花括號結尾「}」,中間有任意數量其它字符,且其它任意字符儘量少。咱們的正則表達式就能夠寫成「\{.*?\}」,它能夠匹配如下字符串中的「abcde{fghi{jklmn}op}xyz」。
正則表達式:「\\.*?\\」 表示已「\」開頭和「\」結尾中間爲任意數量且儘量少的其它字符。它能夠匹配如下字符串中的「abcde\fghi\jklmn\op\xyz」
2.10 零寬斷言
在某些特殊的狀況下,若是咱們只是想要匹配某個字符有(或者沒有)出現,但並不想去捕獲它的時候,咱們就須要零寬度斷言。零寬度斷言和\b等零寬度字符同樣,都是匹配一個位置,並不消耗字符,但零寬度斷言能夠是由表達式構成,功能也就更增強大。零寬度斷言分爲四種狀況。
零寬度正預測斷言
「預測」表示向匹配內容的後方看,「正」表示匹配的意思
通常格式:「exp1(?=exp2)」
含義:匹配文本串中符合正則表達式exp1的內容,且文本串中已匹配exp1的字符串的後面必須匹配exp2,但不消耗文本串中匹配exp2的字符,且結果中不捕獲exp2匹配的內容。不消耗匹配exp2的字符的意思是,下一次搜索從文本串中匹配exp1的後面開始,而不是從匹配exp2的後面開始。注意,exp2右括號後面通常不能再跟正則表達式不然,不會匹配到任何東西。
例子:下面的正則表達式會匹配一個單詞,且這個單詞必須以ing結尾。文本串中捕獲的內容用紅色標示,綠色表示正預言的匹配。
正則表達式:"\b\w+(?=ing\b)"
文本串內容:"i am singing while you are dancing"
注意,正則表達式不能寫成"\b\w+(?=ing)\b",這樣不會匹配任何字符串,由於不存在任何一個字符串後面是ing,同時又要求ing是結束的邊界(因爲不消耗文本串中的ing)。
同理,"\b\w+(?=ing)ing" 等價於 "\b\w+ing"
零寬度正回顧斷言
「回顧」表示向匹配內容的前方看,「正」表示匹配的意思
通常格式:「(?<=exp2)exp1」
含義:匹配文本串中符合正則表達式exp1的內容,且文本串中已匹配exp1的內容前面必須匹配exp2,但在結果中不捕獲exp2的匹配內容。注意,exp2左括號前面不能再跟正則表達式不然,不會匹配到任何東西。
例子:下面的正則表達式會匹配一個單詞,且這個單詞必須以anti開頭。文本串中捕獲的內容用紅色標示,綠色表示正回顧的匹配。
正則表達式:"(?<=\banti)\w+\b"
文本串內容:"this is an antibody, not an antivirus"
注意,正則表達式不能寫成"\b(?<=anti)\w+\b",緣由和正預測是同樣的,由於「\b」和「anti」是互斥的,也就是說沒有一個字符同時知足即便「\b」又是「anti」。
零寬度負預測斷言
「預測」表示向匹配內容的後方看,「負」表示不匹配的意思
通常格式:「exp1(?!exp2)」
含義:匹配文本串中符合正則表達式exp1的內容,且匹配exp1的內容後面不能匹配exp2。和正預測不一樣,咱們通常能夠構成以下的正則表達式exp1(?!exp2)exp3,只要exp2和exp3不相同就不會構成互斥。
例子:下面的正則表達式會匹配一個單詞,且這個單詞不能以ing結尾。文本串中捕獲的內容用紅色標示。
例子:下面的正則表達式會匹配一個單詞,且這個單詞不能以anti開頭。文本串中捕獲的內容用紅色標示。
正則表達式:"\\b(?!anti)\\w+\\b"
文本串:"this is an antibody, not an antivirus"
零寬度負回顧斷言
「回顧」表示向匹配內容的前方看,「負」表示不匹配的意思
通常格式:「(?<!exp2)exp1」
含義:捕獲文本串中符合正則表達式exp1的內容,且捕獲的內容前面不能匹配exp2。和正回顧不一樣,咱們通常能夠構成以下的正則表達式「exp3(?<!exp2)exp1」,只要exp2和exp3不相同就會構成互斥。
例子:下面的正則表達式會匹配一個單詞,且這個單詞不能以ing結尾。文本串中捕獲的內容用紅色標示。
正則表達式:"\\b\\w+(?<!ing)\\b";
文本串:"i am singing , you are danceing";
對同一個正則表達式,從鍵盤上輸入的形式和程序中由字符串表示的正則表達式的形式是不一樣的。好比咱們最開始舉例時使用的正則表達式 \d{4}-\d{2}-\d{2} ,在JAVA中用字符串表示的形式以下
String reg = 「\\d{4}-\\d{2}-\\{2}」
由於在字符串中,須要用兩個「\\」表示一個「\」
JAVA中使用正則表達式主要涉及到兩個類,一個是Pattern類,另外一個是Matcher類,他們都位於java.util.regex包中。Pattern主要的功能就是將正則表達式轉換成NFA(不肯定有限自動狀態機)或者DFA(肯定有限自動狀態機)。Matcher的做用是經過Pattern產生的FNA或DFA對文本串進行匹配。
Pattern類的構造函數:
public static Pattern compile(String regex)
public static Pattern compile(String regex, int flags)
第二個構造函數中的flag,能夠是下列屬性值的組合,它們會影響匹配結果。
|
CANON_EQ Enables canonical equivalence. |
|
CASE_INSENSITIVE 大小寫不敏感 |
|
COMMENTS 容許正則表達式中出現註釋 默認狀況下正則表達式中不容許出現正則表達式規定的註釋 |
|
DOTALL 「.」能夠匹配任何字符 默認狀況下不能匹配 「\r\n」和「\n」 |
|
LITERAL 該模式下將轉義字符就表示字符自己 「\\d」就表示一個「\」和「d」而不表示數字的字符集 |
|
MULTILINE 多行模式:^表示字符串中每一行的開始,$表示字符串中每一行的結束 默認狀況下:^表示字符串的開始,$表示字符串的結束 |
|
UNICODE_CASE Enables Unicode-aware case folding. |
|
UNICODE_CHARACTER_CLASS 使用該選項使得原先已定義好的字符集兼容Unicode編碼 |
|
UNIX_LINES 類Unix下的換行:「\n」 默認狀況下使用Windows下的換行,即:「\r\n」或者 「\n」 |
Pattern類下還有一個matcher方法,咱們經過該matcher方法產生Matcher對象,該方法參數表示待匹配的文本串。
public Matcher matcher(CharSequence input)
Matcher下的find方法用於對文本串的匹配,若是發現匹配就返回真,當再次調用find函數時,會從上次已匹配的位置繼續搜索。
Matcher下的group方法用於返回匹配的字符串,start和end方法用於返回匹配的字符串在文本串中的開始和結束位置,注意不包含結束位置。
package javalearning; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegularExpTest { public static void main(String[] args){ String reg = "[a-z]((?<digtal>\\d+)(b|c)d)[A-Z]"; String txt = "zdfasdfre1234bdXrt"; Pattern p = Pattern.compile(reg); Matcher m = p.matcher(txt); while(m.find()){ System.out.println("--------匹配結果----------"); System.out.printf("[%2d, %2d) : %s\n", m.start(), m.end(), m.group()); System.out.println("--------自動命名組匹配結果--------"); for(int i = 0; i < m.groupCount(); i++){ System.out.printf("group %2d [%2d, %2d) : %s\n",i, m.start(i), m.end(i), m.group(i)); } System.out.println("--------自定義命名組匹配結果--------"); System.out.printf("digtal [%2d, %2d) : %s\n", m.start("digtal"), m.end("digtal"), m.group("digtal")); System.out.println(); System.out.println(); System.out.println(); } } }
運行結果
--------匹配結果---------- [ 8, 16) : e1234bdX --------自動命名組匹配結果-------- group 0 [ 8, 16) : e1234bdX group 1 [ 9, 15) : 1234bd group 2 [ 9, 13) : 1234 --------自定義命名組匹配結果-------- digtal [ 9, 13) : 1234 |
4. 參考內容
[1] 30分鐘學會正則表達式
[2] 正則表達式在線測試工具
[3] 正則表達式在線測試工具