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