【前言】 1.此文針對,正則表達(dá)式的初學(xué)者,老鳥請飄過。 正則表達(dá)式的初學(xué)者,常遇到的情況是,對于相對復(fù)雜一點(diǎn)的正則表達(dá)式,覺得很難理解,很難看懂。 2.此文目的,之前你看不懂,看了此教程后,就基本掌握了,看懂復(fù)雜正則表達(dá)式的思路。 這樣就可以通過自己的能力,一點(diǎn)點(diǎn)把復(fù)雜的正則表達(dá)式,一點(diǎn)點(diǎn)拆分,一點(diǎn)點(diǎn)分析,知道完全理解。 3.在看此文之前,肯定需要你本身對于正則表達(dá)式,已經(jīng)有了一些基本的基礎(chǔ), 比如知道點(diǎn)’.’表示任意字符,星號’*’表示0或多個之類的含義,這樣才有看此文的基礎(chǔ)。 關(guān)于正則表達(dá)式方面的教程和資料,需要的可以去看我整理的一些資料: 【總結(jié)】關(guān)于(C#和Python中的)正則表達(dá)式 java中的正則表達(dá)式:java.util.regex 【如何看懂復(fù)雜的正則表達(dá)式】 基本思路:拆分->各個擊破 解釋: 先將一個,很長的,很復(fù)雜的正則表達(dá)式,從左向右,一點(diǎn)點(diǎn)讀取,分析,一點(diǎn)找到某部分的內(nèi)容,是一個邏輯概念上的獨(dú)立的一塊,就暫時拆分出來,如此,一點(diǎn)點(diǎn)把復(fù)雜的正則表達(dá)式,拆分成很多個邏輯上獨(dú)立的小塊, 然后針對每個小塊的表達(dá)式,再去分析其含義 每個小塊的正則表達(dá)式都搞懂后 把和所有的含義,合并出一個整體的含義 最后就可以實現(xiàn),用人類的語言,把對應(yīng)的復(fù)雜的正則表達(dá)式,一點(diǎn)點(diǎn)解釋出來了,即: 把,之前看不懂的,復(fù)雜的正則表達(dá)式,翻譯成,人類可以看懂讀懂的語言(至少先讓你自己讀懂看懂) 在舉例分析之前,需要知道一些前提: 1.任何復(fù)雜的正則表達(dá)式,都是由寫正則表達(dá)式的人,從簡單到復(fù)雜一點(diǎn)點(diǎn)寫出來的。 所以,理論上,如何讀懂復(fù)雜的正則表達(dá)式,也就是一個反向解析的過程,即將復(fù)雜的拆分成多個簡單的,功能上,邏輯是獨(dú)立的子表達(dá)式,然后再去分析其含義,最終再合并出來整體的,復(fù)雜的含義。 2.正則表達(dá)式,即使各種語言的正則表達(dá)式的庫函數(shù),去解析的時候,也是從左到右,一點(diǎn)點(diǎn)分析,一點(diǎn)點(diǎn)拆分,將復(fù)雜的差分成多個子表達(dá)式,以實現(xiàn),計算機(jī)語言內(nèi)部,去理解此表達(dá)式的。 此處,只是通過人類的方式,手動從左到右,一點(diǎn)點(diǎn)分析而已,也算是和計算機(jī)語言識別正則的類似的過程。 3.雖然正則表達(dá)式,不同的語言,具體的寫法,有些略微的差別,但是本質(zhì)上的,絕大部分的正則表達(dá)式的寫法,都是基本類似的。 【舉例說明,如何實現(xiàn)拆分復(fù)雜的正則表達(dá)式】 舉例: /^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$/i正則表達(dá)式表示什么意思? 首先,對于拿到這個,看似很繁瑣的字符串: /^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$/i 作為,相對比你熟悉正則的我,一看就知道,是PHP或Perl一類的語言中的正則表達(dá)式,因為這里是: /xxx/i 的格式,其中xxx表示真正的正則表達(dá)式本身,而后面的i表示ignoreCase,即忽略大小寫的意思。 而如果你只是熟悉其他如Python等語言的正則表達(dá)式,則此處無需太關(guān)心那兩個斜杠,可以將其理解為,類似于Python中的這樣的寫法: re.match("xxx", re.I) 其中的xxx,是此處真正的正則表達(dá)式: ^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$ 而re.I即re.IGNORECASE,表示忽略大小寫的意思。 接下來,就來分析此處的xxx,即: ^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$ 的完整的含義: 對于我來說,看到此正則表達(dá)式: ^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$ 后,我可以直接將其,按照之前所介紹的方法,直接拆分出對應(yīng),幾個子表達(dá)式,其中每個子表達(dá)式,相對來說,是邏輯上獨(dú)立的,或者是沒關(guān)系,關(guān)系不大的各個小的正則表達(dá)式。 先說拆分的結(jié)果如下:
但是,作為讀者的你,肯定看了會說,我怎么才能,像我這里一樣,一次性就看出,如何將上述復(fù)雜的正則表達(dá)式,一下次分出這8部分,即(將上面那個復(fù)雜的正則表達(dá)式)大卸八塊呢?^_^ 那么此處,就來介紹一下,基本的思路,或者說,我是怎么實現(xiàn)此過程的: 【如何拆分正則表達(dá)式: ^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$】 首先,看到這么一堆的復(fù)雜的字符,其實我也不可能立刻實現(xiàn),全部拆分出來。 我也是一點(diǎn)點(diǎn),像之前介紹的方法和思路一樣,是從左到右,一點(diǎn)點(diǎn)去,識別,區(qū)分,然后一點(diǎn)點(diǎn)分出來這么多個子表達(dá)式,子部分的: 1.比如,首先,我從左往右看的話,第一個看到的是’^’,對此,對于有了正則表達(dá)式最最基礎(chǔ)的你,應(yīng)該知道,這個是匹配字符串的開始的; 而很明顯,對于’^’,此處,一般不會,此處也沒有后面有啥限定符,即沒有和其他字符,去搭配使用。 所以,此’^’,就是我們所拆分出來的,第一個,相對邏輯上獨(dú)立的,子正則表達(dá)式,所以就可以寫出第一個小子表達(dá)式了:
2.然后接著來分析,接下來是左中括號'[',而對于左中括號,還是那句話,作為已有正則表達(dá)式的基礎(chǔ)的你,知道其,一般來說都是和另外一個右中括號’]’去搭配使用,并且左右中括號里面,也會有一些字符,以表示中括號內(nèi)的字符,所組成的集合,即類似于[xxx]的形式,對此,接著往后看,可以說,此處還是很簡單的,就看到了后面還有’A-Z]’,正好和'['組成了'[A-Z]’,正好符合我們所理解和期望的[xxx]的形式。 而此處,很明顯,就是A-Z,對應(yīng)著正則表達(dá)式的語法,在中括號內(nèi),可以通過短橫線鏈接起始字符,表示一段范圍內(nèi)的字符,此處即通過A-Z表示,A,B,C,。。。,X,Y,Z,這26個大寫字母。 所以,此處,看似,也就很清楚了,覺得第二個子正則表達(dá)式,就是[A-Z]了。 而作為比你經(jīng)驗稍多的我,要告訴你,其實你此處這樣的想法,是嚴(yán)謹(jǐn)?shù)?,因為,對于,中括號?nèi)部表示字符集合,[xxx]的寫法,往往后面還會跟著一些限定符,去表示此集合字符的個數(shù)方面的限定,比如加號’+’表示去匹配,往后數(shù),盡可能多個,比如表示最少2個,最多5個的'{2,5}’等等。 而此處呢,算是巧了,后面實際上,是沒有這類限定符的,因為我們看到了,后面只有冒號’:’,而冒號,此處,正如按照正常邏輯所理解的一樣,就是表示匹配冒號字符’:’本身而已。 所以,此處,由于巧了,后面沒有字符個數(shù)方面的限定符,所以,第二個子正則表達(dá)式,正好就是[A-Z]本身而已,所以,接著寫出,我們已經(jīng)拆分出來的,共兩個子正則表達(dá)式了:
3.上面已經(jīng)分析了,此處后面跟著的字符,是冒號這個字符’:’,同理,由于后面沒有看到其他的加號’+’之類的限定符,所以此處,冒號本身,就是表示一個完整的子正則表達(dá)式,去匹配單個的冒號了。 所以,此處第三個,子正則表達(dá)式也就是此冒號字符本身了。所以,現(xiàn)在共拆分出來三個子正則表達(dá)式了:
4.可以看到,冒號后面是個反斜杠’\’,而看到反斜杠,作為已了解正則表達(dá)式的語法的你,應(yīng)該知道,正則表達(dá)式中,會有很多’\x’其中x是某個字母的形式,而不同的字符,組合出來的,表示不同的各種含義,比如常見的\d表示數(shù)字0-9等等。 而此處,看到的是反斜杠后面’\’后面,又跟了個反斜杠’\’,對此,根據(jù)正則表達(dá)式的語法,則是表示反斜杠這個字符本身,就是想要去匹配一段字符串中,是否有反斜杠這個字符本身。 然后接著往后看,是{1,2},很明顯,是之前已提到多次的,限定符,作用是,限制(前面的字符的)個數(shù)是,至少1個,最多2個。所以此處就是去限定前面的,反斜杠字符本身,所以加起來,就是\\{1,2},而對應(yīng)的含義也就是 去匹配,至少一個反斜杠,最多2個反斜杠。 所以,目前已拆分出共4個子表達(dá)式了:
5.再往后面分析,是左中括號'[',根據(jù)正則表達(dá)式的語法,和前面已經(jīng)討論過一次中括號的用法,我們可以知道,后面一定還有一個右中括號,所以,把左右中括號,以及中間內(nèi)容,都一起寫出來,就是: [^/:\*\?<>\|] 但是,對于中括號中間的這么一堆字符: ^/:\*\?<>\| 至少看起來,也還是比較復(fù)雜的。 再但是呢,對于已經(jīng)了解正則表達(dá)式語法的你,應(yīng)該知道,中括號內(nèi),表示取反的寫法是,對應(yīng)的字符或字符集,在其前面,添加上那個特殊字符,向上的箭頭,此處叫做插入字符’^’,表示針對某個,或某些字符,取反的意思,即匹配除了這些字符之外的那些字符。 而此處,就是對應(yīng)的 對于 /:\*\?<>\| 前面加上個插入符號’^’,變成: ^/:\*\?<>\| 表示,匹配,除了 字符組合: /:\*\?<>\| 之外的字符。 而此處的字符組合: /:\*\?<>\| 其實就是一堆的字符,一點(diǎn)點(diǎn)寫出來的,其詳細(xì)含義,我們后續(xù)再分析。 此處還沒完,因為此處的[^xxx]的形式之后,還有個加號’+’,對應(yīng)含義也很明確,就是前面那種字符,即除了/:\*\?<>\|之外的字符,的個數(shù),此處通過加號去限定為,至少是1個,可以更多個,即>=1的個數(shù)。 所以,算是[^xxx]+的形式了,其中xxx是/:\*\?<>\| 因此,此處已經(jīng)共分析出5個子表達(dá)式了:
6.再往后看,就是一個反斜杠’\’加上一個點(diǎn)’.’,即’\.’,其表示點(diǎn)字符本身,這點(diǎn)你也應(yīng)該在學(xué)習(xí)正則表達(dá)式基本語法的時候,有所了解。 此處再多解釋一句就是,之所以不直接寫點(diǎn)’.’,是因為字符點(diǎn)’.’本身,在正則表達(dá)式中,是匹配任意一個單個字符的意思,而想要匹配這樣的,在正則表達(dá)式中被用于表示的含義的字符的時候,就需要用到反斜杠,反斜杠用來表示所謂的轉(zhuǎn)義。 在正則表達(dá)式中,常見的就有:
因此,此處一共已拆分出6個子正則表達(dá)式了:
7.再往后看,后面是一個左括號'(',很明顯,此處后面肯定有一個右括號,和此處的左括號聯(lián)合起來,表示一個組group。 此處,很簡單,就可以看出來是 (jpg|gif|png|bmp) 注:更復(fù)雜的正則表達(dá)式,可能會出現(xiàn)多個group嵌套的情況,即括號內(nèi)嵌套括號的情況,此時,此種拆分方法仍然有效,還是找到最開始的左括號,此時對于括號層次來說肯定是最外層,所匹配的那個的最外層的右括號,那這一部分拿出來,繼續(xù)分析即可。如果存在更多曾的括號嵌套括號,仍然是找到對應(yīng)匹配的括號即可。 而對于此處的group組: (jpg|gif|png|bmp) 的含義,后面再詳細(xì)分析。 此時,也已經(jīng)拆分出來,共7個子表達(dá)式了:
8.最后,還剩下一個美元符號’$’,表示匹配字符串末尾,這個很好理解。不多解釋。 此時,就已經(jīng)實現(xiàn)了,把上述的一個復(fù)雜的正則表達(dá)式,拆分成多個邏輯上獨(dú)立的,共8個,子正則表達(dá)式了:
看到這里,對于如何從左往右看,一點(diǎn)點(diǎn)根據(jù)邏輯組合,去拆分成多個子表達(dá)式,的總體方法和思路,應(yīng)該大概清楚了。 余下的事情,就是自己通過多讀多看多學(xué)習(xí),去了解別人寫的正則表達(dá)式,用此套分析方法,去拆分了。 知道了方法,加上盡量多的練習(xí),自然會對正則表達(dá)式,越來越熟悉,越來越理解的。 此處,對于此正則表達(dá)式的分析,還沒完。因為還有幾個字正則表達(dá)式的含義,沒有完全分析透徹。 下面先來總結(jié)一下,已經(jīng)知道的,各個子表達(dá)式的含義:
很明顯,還剩兩個我們沒有分析,下面就來詳細(xì)分析解釋其含義: 1./:\*\?<>\| 的含義 其實,理論上,對于這樣的字符串: /:\*\?<>\| 其實也是繼續(xù)將其按照上述方法,去將其拆分為不同的子表達(dá)式。 只是由于此處看似復(fù)雜,其實還是很簡單,所以,直接分析一下,即可看出其含義。就不詳細(xì)拆分了。 此處,根據(jù)字符本身含義,依次是:
所以,此部分 的總體含義就是: 字符,斜杠,冒號,星號,問號,小于號,大于號,豎線,這些字符(集合) 而放到[^xxx]里面,變成: [^/:\*\?<>\|] 的意思就是 除了字符: 斜杠,冒號,星號,問號,小于號,大于號,豎線 這些字符之外的,其他的任意字符 而再加上之前的加號’+’去限定其個數(shù)是最少1個,>=1個,變成: [^/:\*\?<>\|]+ 所表示的意思就很清楚了: 去匹配 盡可能多個字符,這些字符是: 除了字符: 斜杠,冒號,星號,問號,小于號,大于號,豎線 之外的,其他的任意的字符 到此,對此 [^/:\*\?<>\|]+ 的含義,才算基本明確。 而如果你本身對于windows等操作系統(tǒng)對于文件名或者路徑字符的限制有了解的話,你會發(fā)現(xiàn),這基本上就是 我們所常見的,對于你在windows中,問文件或文件夾命名時,其所提示的,不允許你名字中包含這類: 斜杠,冒號,星號,問號,小于號,大于號,豎線 即: /,:,*,?,<,>,| 而此時,如果你稍微會點(diǎn)舉一反三/觸類旁通的思想的話,就會聯(lián)想到,此處去匹配的東西,很可能是文件或文件夾的名字方面的東西。 2.jpg|gif|png|bmp 的含義 此處的正則表達(dá)式,很明顯看出就是: xxx|xxx|xxx 的格式,其中xxx分別是,具有不同可能的字符串,即多個可能性之一 對應(yīng)的,其所表達(dá)的意思是,去匹配: 要么是jpg,要么是gif,要么是png,要么是bmp (除了這幾種可能外,其他的都不匹配) 對于這種匹配多種可能性的正則的寫法,想要深入了解的話,可以參考教程: 【教程】詳解Python正則表達(dá)式之: '|’ vertical bar 豎杠 所以,此時,我們就可以把每部分的內(nèi)容的含義,都完整分析出來了:
所以,把這些各個子正則的含義,連接在一起,就可以用語言表示為: 去匹配一個字符串, 該字符串,開頭部分,就一個字母,該字母可能是從A到Z的任何一個字母, 后面跟著一個冒號, 再后面是1個或2個反斜杠, 然后是至少一個,但盡量多的,除了斜杠,冒號,星號,問號,小于號,大于號,豎線之外的其他的任意字符, 然后是字符點(diǎn), 然后以jpg,gif,png,bmp中的其中一個而結(jié)尾 而對應(yīng)的,由于之前還有flag標(biāo)志,表示忽略大小寫,則所匹配的內(nèi)容,就是上述內(nèi)容的表述,再加上一個,期間部分大小寫,就可以了。 所以,最終所要表述的含義就是: 去匹配一個字符串, 期間字母不分大小寫, 該字符串,開頭部分,就一個字母,該字母可能是從A到Z的任何一個字母, 后面跟著一個冒號, 再后面是1個或2個反斜杠, 然后是至少一個,但盡量多的,除了斜杠,冒號,星號,問號,小于號,大于號,豎線之外的其他的任意字符, 然后是字符點(diǎn), 然后以jpg,gif,png,bmp中的其中一個而結(jié)尾 由此,我們可以隨便寫出來一個,符合該規(guī)則的字符串,比如: a:\123abc.jpg a:\\123abc.bmp a:\\123abcdef.jpg A:\\123abcdef.jpg E:\\abc123.png 等等,諸如此類的字符串。 此時,已可很明顯看出來其用意了,其就是要去匹配: Windows類系統(tǒng)(如XP,Win7等)中,本地的某個磁盤分區(qū)根目錄下的某張圖片而已。 至此,算是完整的,從開始的,無法用肉眼一眼就看出來含義的,那個復(fù)雜的,正則表達(dá)式,將其一點(diǎn)點(diǎn)拆分,分成多個子表達(dá)式,各個擊破其子表達(dá)式的含義,最終再把每個子表達(dá)式的含義合成在一起,再加上對應(yīng)的flag標(biāo)志的影響,最終生成了復(fù)雜表達(dá)式的最終含義,以及,用文字描述出來,最終,領(lǐng)悟和理解,原始的正則表達(dá)式,所要表示的含義。 通過此分析過程可見,其實再復(fù)雜的表達(dá)式,也都是可以通過拆分的方法,由繁化簡,而逐個擊破,了解細(xì)節(jié)的含義,再整合出宏觀上的整體的含義,最終搞懂完整的表達(dá)式的含義的。 只是過程,或繁或簡,取決于表達(dá)式本身的復(fù)雜程度,以及你本身所對正則表達(dá)式的理解和掌握的程度。 【總結(jié)】 千言萬語總結(jié)出幾句話: 1. 對于復(fù)雜的正則表達(dá)式,即使從左往右,一點(diǎn)點(diǎn)分析,拆分出多個子正則表達(dá)式,然后各個擊破,搞懂其含義,最后再合成一個總體的含義,即可實現(xiàn),將復(fù)雜的正則表達(dá)式,翻譯成人類可以讀懂的含義了。 2.再復(fù)雜的正則表達(dá)式,花足夠的時間去分析,都是能搞懂的。 只不過具體要花多長時間,則因人而異。 3.想要盡快的,準(zhǔn)確的理解原正則表達(dá)式所要描述的含義,還是要多多練習(xí),最終達(dá)到熟能生巧,以至于觸類旁通的效果。 |
|