雖然本系列文章後續會簡單介紹正則表達式的一些基礎知識,但主要限於本系列文章所想強調的要點,所以本系列文章並不適合用於入門。php
若你是對正則表達式沒有任何概念的初學者,建議至少先閱讀網上備受推崇的《正則表達式30分鐘入門教程》,時間容許的話最好再閱讀《正則表達式必知必會》(才130多頁,寫得很是基礎,快的話一天可輕鬆讀完)。這樣在創建了對正則表達式的基本認識以後,再來閱讀本系列文章,才更爲合適。html
最後,文中如有錯漏,還請直接招呼板磚,不用客氣😊java
1、緣起python
1.git
前面在字符編碼系列文章的前言中曾說過,相似於字符編碼這樣基礎、重要、應用普遍而又特別容易讓人困惑的主題還有字節序(即大小端表示)、正則表達式以及浮點數實現、日期時間處理等等。其中,字節序、正則表達式跟字符編碼的關係很是密切。字符編碼以及字節序的問題已經在字符編碼系列文章中介紹過了,這個系列再來討論正則表達式。程序員
不一樣於字符編碼,正則表達式目前市面上並不缺少專業著做,好比那本被譽爲正則表達式學習聖經的《精通正則表達式》就很值得一讀,另外該書的譯者餘晟先生所寫的《正則指引》也不錯;若是僅用於入門,則《正則表達式必知必會》確定不能錯過,還有網上流傳極廣的《正則表達式30分鐘入門教程》也是不錯的入門資料。github
可是,結合我自身痛苦的正則表達式學習經歷和運用體會,僅有這些是遠遠不夠的。記得被你們稱之爲「輪子哥」的大神級程序員vczh在知乎上說過,當初被正則表達式虐得一氣之下,乾脆本身寫了一個正則引擎(源碼託管在Github上),纔算真正完全搞懂正則表達式(因而被戲稱爲「一言不合」就造輪子)。固然不是每一個程序員都能如今生猛,但即使都有這麼生猛,彷佛也不必都像「輪子哥」這樣本身去再造一個「輪子」。正則表達式
那到底應該怎樣才能最高性價比地掌握正則表達式這個神器呢?這正是我寫這個系列文章的目的。算法
2.數據庫
正則表達式,一聽就是個很是專業的術語,對於大多數人而言徹底不知道這跟本身有啥關係。但事實上,只要是平時用Word寫點東西的人,均可能用得上。
沒錯,Word就支持正則表達式,雖然所支持的功能遠遠不如常規的正則表達式強大,語法上也有很大的不一樣,於是只能算得上是準正則表達式或類正則表達式;但對我而言,只要是用Word寫文章,就已經沒法想象沒有正則表達式的情形了。
固然,在Word中使用正則表達式,只能算是小試牛刀。正則表達式更大的用武之地在於各大經常使用高級編程語言、編輯器以及grep、sed和awk等命令行文本處理工具中,用以處理文本的查找、提取、替換、切分等操做。
3.
正則表達式是典型的那種沒用過的話,不以爲對本身有什麼影響,但是一旦用過了,就再也回不去了的神器。固然,我這裏所說的「用過」,不是指簡單用用一些基本功能,而是指可以熟練運用其基本功能和高級功能。用得越熟練,就會越驚歎於其強大與神奇。
看到這裏,我相信某些學過正則表達式、會使用一些基本功能的童鞋,內心或許在犯嘀咕了:神器是神器,可這玩意兒看起來就像天書同樣,也太難學、太難懂了,要達到熟練運用的程度,談何容易!短短的一個正則表達式,或許不到10個字符,其中的每一個字符都認識,但連在一塊兒,卻越看越迷惑,越想越迷糊……
是的,正則表達式既然被捧上了神器級別的高度,天然是有着至關強大的功能,這固然就意味着其有很是深厚的內涵,也就意味着有不少須要注意的細節。
4.
注意,我這裏沒有說正則表達式是因爲複雜而難以理解,這是由於,深厚的內涵不等於複雜,細節不少不意味着難以理解。看到這裏,或許有人有意見了,正則表達式還不算複雜?還不夠難理解?你秀智商呢,仍是秀優越感呢?哦,相信我,其實這二者我都不太沾邊。智商我也只是中等而已,不然早就不在這裏碼字了;而優越感則更提不上——既不高也不富更不帥,何來優越感?!
其實,我真正想說的是,繁複或許是真的,雜亂倒未必。所以,簡單地說正則表達式複雜,彷佛不夠準確而客觀。正如跟一個牛叉而又性格獨特的人(廢話,真正牛叉的人基本上都有獨特的性格)打交道,關鍵不在於糾結其性格的獨特、脾氣的古怪,而是重在充分了解並理解其獨特的性格、古怪的脾氣,而後在此基礎上與他/她進行良好的溝通,以便能好好發揮其牛叉之處。
5.
學習並熟練掌握正則表達式的過程也是如此——關鍵在於先要摸透其「性格」到底獨特在哪裏,其「脾氣」又究竟古怪在何方。一旦摸清楚了其「性格」,其「脾氣」,學習起來就事半功倍了。
所以,我下面準備從我本身的角度,先嚐試着來分析一下正則表達式那獨特的「性格」與古怪的「脾氣」,看看究竟爲何正則表達式給那麼多人的感受都是那麼難以「親近」。
2、正則表達式爲何難學?
1.
對於正則表達式的分析和解讀,目前大多數文章和書籍多集中在正則表達式自身,好比對正則表達式的各個元字符、元轉義序列以及匹配原理的分析和解讀上。
固然,這些天然也是頗有必要的,並且是學習的主要內容,是理解正則表達式所必需的。然而,不少人在看了大量這類文章和書籍以後,仍然以爲正則表達式很難看懂,很差理解,常常有一種智商被碾壓的即視感。
2.
難道真的是正則表達式的學習者智商不夠嗎?其實,理解一個事物,都應該有兩個維度,或者說兩個層面:
一是,深刻到該事物自己裏面去理解;
二是,跳出到該事物外面,站在更高的一個維度或層面來理解。
正如蘇軾那首著名的哲理詩《題西林壁》所說的,「不識廬山真面目,只緣身在此山中」。不少時候每每是這樣,當你只從該事物自己來看的話,就如在雲裏霧裏,是遠遠不夠的;而一旦跳出到該事物以外,站在更高的一個角度來看,則又正如王安石的《登飛來峯》中所說:「不畏浮雲遮望眼,只緣身在最高層」。
3.
對正則表達式而言,前者正是目前大多數文章和書籍在作的;然後者,卻不多有文章和書籍可以跳出正則表達式,站在更高的維度或層面來分析和解讀正則表達式。這裏就包括了被譽爲正則表達式學習聖經的《精通正則表達式》,以及該書的譯者餘晟先生所著的《正則指引》兩書。
這裏須要特別強調一下的是,我絕沒有貶低上述這兩本專業著做及其做者和/或譯者之意,並且偏偏相反,這兩本專著正是本系列文章的重要參考書。尤爲不管是做爲《精通正則表達式》的譯者,仍是做爲《正則指引》的著者,餘晟先生都絕對稱得上是專業而又嚴謹的。
4.
那麼,前面所謂「更高的維度或層面」,到底指的是什麼呢?那就是,從編程語言發展史以及編程範式的角度來看正則表達式。什麼?正則表達式居然也算得上是一門正式的編程語言嗎?別急,請繼續往下看。
正則表達式有一個很是明顯的特色:高度簡潔、高度抽象。正則表達式中短短的幾個字符,或許就表明了一段複雜的處理邏輯和匹配算法。
5.
咱們知道,程序代碼是對現實事務處理邏輯的抽象,而正則表達式則是對複雜的字符匹配程序代碼的進一步抽象;也就是說,高度簡潔的正則表達式,能夠認爲其背後所對應的是字符匹配程序代碼,而字符匹配程序代碼,背後對應的是字符匹配處理邏輯。
所以能夠這麼認爲,字符匹配處理邏輯,抽象爲字符匹配程序代碼;字符匹配程序代碼,再進一步抽象爲高度簡潔的正則表達式。因此說,高度簡潔的正則表達式也是高度抽象的。
6.
事實上,從編程語言發展的角度來看,正則表達式也是一種編程語言,並且是屬於第4代語言(4GL)——面向問題語言(第1代語言爲機器語言——由0和1組成的位串,第2代語言爲彙編語言——用接近於英語單詞的助記碼mnemonic code來代替由0和1組成的位串,第3代語言爲高級語言——用接近於天然語言的語法元素編寫程序,如C/C++、Java、C#、Perl、Python、PHP、JavaScript等語言,第4代語言爲面向問題語言——用針對問題領域專門設計的語法元素編寫程序或表達式,如SQL、SAS、SPSS、LaTeX、Regex(即正則表達式)等,第5代語言爲人工智能語言——Prolog、Mercury、OPS5等;不過,從第4代語言到第5代語言的演化還不是很清晰,目前學術界爭議較大,這裏不做討論)。
【注:這裏強烈推薦鄭暉先生所著的《冒號課堂——編程範式與OOP思想》一書,該書既以宏觀視角,縱向審視了編程語言發展簡史,橫向比較了各種編程語言的特色;又以微觀實踐,娓娓道來各編程範式的優劣得失,更是深刻探討了面向對象編程的方方面面,是不可多得的一本中文原創計算機專著。這裏是我在豆瓣寫的該書書評,供參考。】
第4代語言相對於第3代語言,更專一於其所應用或者說其所適用的某個特定的業務邏輯和問題領域。程序員主要負責分析問題,以及使用第4代語言來描述問題,而無需花費大量時間去考慮具體的處理邏輯和算法實現(事實上,最初之因此提出第4代語言的概念,就是但願非專業程序員也能作應用開發,雖而後來的發展事實證實並無很好地實現這一目的)。
7.
從編程範式(Programming Paradigm)的角度上來說,第4代語言屬於聲明式編程範式(Declarative Programming Paradigm),不一樣於重在過程而非目標、重在實現而非描述,必須由程序員明確寫出處理邏輯和算法實現的命令式編程範式(Imperative Programming Paradigm),聲明式編程範式重在目標而非過程、重在描述而非實現,以聲明式語句直接描述問題,專一於問題的分析和表達,而非專一於處理邏輯和算法實現過程,其具體的處理邏輯和算法實現是由語言解析引擎(編譯器或解釋器)來負責的。
固然,這樣一來,這些由語言解析引擎實現的處理邏輯和具體算法其通用性就會較差,只能適用於某些特定業務或特定領域。也正是這個緣由,第4代語言基本都是侷限於某些特定領域的,多被認爲是領域特定語言DSL(Domain Specific Language)。
區別於算法實現可由程序員自由靈活設計的通用編程語言GPPL(General-Purpose Programming Language,做爲第3代語言的高級語言基本上都屬於通用編程語言),領域特定語言DSL的算法基本上由語言解析引擎自動實現,程序員靈活設計、自由發揮的空間很小,所以DSL幾乎沒有通用性(並且DSL大都是非圖靈完備的語言),只能專用於解決特定業務方向和業務領域的問題。
好比,SQL是專用於數據庫操做的語言、SAS和SPSS是專用於統計分析的語言、LaTeX是專用於排版的語言,而正則表達式Regex(Regular expression)則是專用於處理字符匹配的語言。
8.
理解了這一點,就比較容易理解正則表達式是字符匹配處理邏輯的抽象;更進一步地來講,正則表達式中的某些元字符與特殊結構,可理解爲某種具體的程序邏輯和算法的體現。
好比,正則表達式中的量詞*這一元字符,就是高級語言的處理邏輯「循環結構」的體現(具體來講量詞*表明的是不定次數循環),而先後多個量詞的嵌套就是多層循環的嵌套;或運算符|這一元字符,就是高級語言的處理邏輯「選擇結構」的體現。
而當或運算符|出如今由量詞*所限定的圓括號中時,其實就是「循環結構」中嵌套了「選擇結構」;而若是進一步地,「循環結構」所嵌套的「選擇結構」中的某個分支,又被某個量詞*所限定,那麼則至關於「循環結構」所嵌套的「選擇結構」中又嵌套了「循環結構」。
理解這一點很是重要,是快速、深刻理解正則表達式的一把鑰匙、一條捷徑。站在編程語言發展史和編程範式的高度,再結合對正則表達式自己原理的深刻理解,裏外結合,高下相較,既登高望遠、一覽衆山小,又洞幽燭微、復觀千水深,正則表達式的奧義,就能盡在掌握之中了。
9.
固然,正則表達式之因此難學、難理解,除了因爲正則表達式做爲一個字符匹配領域的領域特定語言(DSL),具備高度簡潔、高度抽象的特色以外,大體上應該還有如下幾個緣由:
1) 學習者不求甚解,不瞭解正則引擎內部的基本原理
做爲正則表達式的使用者,不須要深刻了解正則引擎內部原理的技術實現細節,那是正則引擎開發者更應該瞭解的;但若徹底不瞭解其基本工做原理和運行機制,也是不足取的。
好比-、+、?、^,尤爲是元字符?,既能夠做爲量詞表示其所限定的子表達式爲可選(即匹配0次或1次),也能夠置於量詞以後表示懶惰匹配,並且還有不少特殊分組結構中用到它,好比(?<name>sub-regex)、(?:sub-regex)、(?>sub-regex)、(?=sub-regex)、(?!sub-regex)、(?<=sub-regex)、(?<!sub-regex)、(?|sub-regex)、(?modifier-modifier)、(?(condition)|)、(?R)、(?num)、(?#comment)等;還記得我本身當初剛開始學習的時候,一看到正則表達式中的問號?,就有一種獨自在風中凌亂的感受。
3) 轉義也是難點
什麼狀況下須要轉義,什麼狀況下不須要轉義,貌似複雜得使人抓狂;固然,實際上是有必定的規律的,掌握了這些規律,再遇到轉義問題,就不至於心潮澎湃了。
4) 學習指望與學習方法不對
不該該指望一次性記住、學會並熟練運用,正確的學習姿式應該是:先簡單入門,對一些基本的規則與元字符大體瞭解一遍,有個印象就好,在須要時再回過頭來看,不用刻意去強行記憶;而後接下來就應該多練、多實踐、多運用,邊學、邊深刻、邊熟練。
5) 有用於入門的好教程、備忘單,也有用於深刻的大部頭專著,但卻缺少好的速查手冊
因爲須要邊學、邊深刻、邊熟練,所以,平時手頭邊更須要的不是簡單的入門教程、備忘單(Cheat Sheet),也不只僅是知識點按學習順序由淺入深地分散於各處的大部頭專著(知識點分散致使查找起來不方便,用於循序漸進地進行學習是不錯,但不夠實用),而是一本按語法元素將知識點綜合在一塊兒進行編排的、在須要回過頭來看時可以隨時快速翻查的速查手冊。這樣,在實踐運用中遇到問題就可方便隨時快速翻查,而這一點偏偏對於正則表達式這種不可能短時間內快速掌握並熟練運用的專業工具的學習與使用很是重要。
6) 沒有使用好的學習工具
你知道regex101.com、RegexBuddy、regexper.com等正則表達式的專業網站和專業工具嗎?這些堪稱學習正則表達式的神器,可令學習事半功倍,但不少人不知道,或知道但不多使用。
3、關於本系列文章的編排設計
1.
本系列有關正則表達式的文章,出自於我本身在學習正則表達式的過程當中所經歷過的真切體會和真實痛點。所以,正如前面所述,採起的編排風格相似於速查手冊。
可是要特別注意,這僅僅是出於邊用邊快速翻查的目的而做出的編排設計,不等因而一般你們所理解的那種簡單解釋一下概念,而後羅列一下功能,再加幾個示例的雞肋般「食之無味,棄之惋惜」的字典式簡易手冊(這種簡易手冊僅供入門使用);更不是將元字符、元轉義序列、特殊結構的簡單解釋編排在一張A4大小紙張上的備忘單(固然,這種備忘單也並不是沒有意義,至少經過一張A4大小的紙張就可快速瞭解正則表達式所支持的語法元素包括哪些,所以本系列文章也會提供幾份我收藏的備忘單供你們參考,但顯然也僅供入門使用)。
這也就是文章名稱中之因此特別強調「刨根究底」,而不是直接名之爲速查手冊、快速參考之類的重要緣由。
2.
所以,本系列文章與相關專著同樣,也一樣會涉及到正則引擎內部的相關匹配原理與匹配機制的解釋(並且還首創性地總結爲了幾大原則,便於「以簡馭繁」、「提綱挈領」地快速掌握要領以便於記憶和理解),只是與其餘專著用專門章節進行介紹不一樣,而是各自糅合於對相關語法元素的解釋之中了。
這種爲了便於快速翻查而沒有將匹配原理與匹配機制予以專章介紹的特殊編排,天然也有其缺點(好比,你可能會在不一樣的語法元素中發現相似的雷同解釋,這或許有重複囉嗦之嫌,但畢竟這符合咱們的編排目的),但問題在於市面上進行專章介紹的專著已經有不少了,再重複它們意義不大;而專門針對前述的正則表達式學習和運用痛點的文章和專著則基本沒有,而這正是本系列文章的意義和目的所在。
3.
也所以,出於更偏向於實踐運用的目的,本系列文章不會花費過多的筆墨在DFA、NFA等過於深刻的正則表達式幕後技術細節的講解上。
事實上,我認爲只要大體瞭解它們的基本原理與工做機制以及二者之間在功能特性上的差別,就徹底能夠熟練掌握並運用正則表達式了,除非你是想本身開發一個正則引擎,實在不必過於陷入DFA、NFA等狀態機(自動機)的技術實現細節中。
雖然前面曾提到過,「輪子哥」vczh爲了完全搞懂正則表達式,硬生生本身從新造了一個「輪子」。然而,要是爲了學個正則表達式,都非要這樣重造輪子,既無可能,也實在沒有必要。
那麼,真的在不重造輪子、不陷入DFA、NFA等技術細節的基礎上,也能搞懂正則表達式?
我本身的體會是,能!本系列文章就是我本身學習心得體會的總結,文章中除滲透了前文所述的「裏外結合,高下相較」這一相對「務虛」的基本思路以外,固然也有相對「務實」的「乾貨」——總結出來的八大原則(包括六大基本原則:最左原則、先到先得原則、最長原則、逐位置依次嘗試匹配原則、總體匹配優先原則、佔有匹配優先原則;以及兩大衍生原則:最左先到先得原則、最左最長原則)、多角度立體的文字講解,以及大量圖示。
所以,我相信經過反覆閱讀本系列文章,再多加練習、勤於實踐,而後在實際運用時再不斷回過頭來隨時翻看,應該徹底能夠熟練掌握這個像毒品同樣會讓人用上癮的神器。
好了,牛皮吹過了,究竟是騾子是馬,後面會拉出來遛遛……
4、下面是正則表達式系列文章將會涉及到的內容:
一)正則表達式簡介
一、什麼是正則表達式
二、爲何使用正則表達式
三、正則表達式簡史
四、正則表達式流派
五、正則表達式基礎
六、八大原則簡介,包括:
六大基本原則:最左原則、先到先得原則、最長原則、逐位置依次嘗試匹配原則、總體匹配優先原則、佔有匹配優先原則
兩大衍生原則:最左先到先得原則、最左最長原則
二)元字符逐個詳解,包括:\、(、)、[、{、.、-、*、+、?、|、^、$,其中-、+、?、^爲多義元字符
三)元轉義序列逐個詳解,包括:
固定字符:\a、\b(字符組內部)、\e、\f、\n、\r、\t、\v(非Perl系)
字符組簡記:\d、\D、\h、\H、\N{}、\p{}與\pP、\P{}與\PP、\s、\S、\v(僅Perl系)、\V、\w、\W
進制轉義字符:\octal-num(Perl系中也可寫做\o{octal-num})、\xhex-num(Perl系中也可寫做\x{hex-num})、\uhex-num(非Perl系,Ruby1.9+等個別語言中還可寫做\u{hex-num})
控制字符:\cX系列
錨點:\A、\z、\Z、\b(字符組外部)、\b{}、\B、\B{}、\G
引用:\num、\g{num}、\gnum、\k{name}、\k<name>、\k'name'
修飾:\E、\F、\l、\L、\Q、\u(僅Perl,不是僅Perl系)、\U
其餘:\C、\K、\N、\R、\X、\<、\>
四)特殊構造(特殊結構)逐個詳解,包括:
字符組[xyz]或[^xyz]、捕獲分組(sub-regex)、命名捕獲分組(?<name>sub-regex)、非捕獲分組(?:sub-regex)、預查分組(即環視分組)(?=sub-regex)或(?!sub-regex)或(?<=sub-regex)或(?<!sub-regex)、固化分組(即原子分組)(?>sub-regex)、嵌入條件分組(?(condition)true_sub-regex|false_sub-regex)、內聯修飾選項與取消內聯修飾選項分組(?modifier-modifier)、註釋分組(?#comment)、分支復位分組(?|sub-regex)、表達式引用分組(?R)或(?num)、平衡分組(?<-name>sub-regex)等
五)匹配模式詳解,包括:i、s、m、x、g等經常使用匹配模式
六)POSIX字符組方括號表達式、排除型POSIX字符組方括號表達式
七)字符組運算:字符組減法運算、字符組邏輯與運算
八)正則表達式各語法元素優先級
參考資料:
一)官方文檔
Perl:
Perl regular expressions (perlre)(英文)
Perl Regular Expressions Reference (perlreref)(英文)
Perl Regular Expression Backslash Sequences and Escapes (perlrebackslash)(英文)
Perl Regular Expression Character Classes (perlrecharclass)(英文)
PCRE:
PHP:
.Net(C#、VB):
Java:
Regular Expressions Tutorials(英文)
JavaScript:
EMCAScript:RegExp (Regular Expression) Objects(英文)
Python2.7:
Regular expression operations(英文)
Python3.4:
Regular expression operations(英文)
Ruby:
Vim:
模式及查找命令 For Vim version 7.4(中文)
Search commands and patterns For Vim version 7.3(英文)
GNU Grep:
GNU Sed:
GNU awk:
二)書籍
《精通正則表達式》英文版及中文版 做者:Jeffrey E·F·Friedl 譯者:餘晟 電子工業出版社 2012-07
《正則指引》做者:餘晟 電子工業出版社 2012-05
《正則表達式必知必會》做者:Ben Forta譯者:楊濤 人民郵電出版社2015-01
《冒號課堂:編程範式與OOP思想》做者:鄭暉 電子工業出版社 2009-10
三)其餘
本系列文章還參考了網上的大量資料,除了少部分資料因爲未做大量修改(但基本上也有少許修改,由於網上文章隨意性較大,不少明顯的筆誤或先後矛盾之處,如若不改反而讓人迷糊)而標明瞭原做者和出處以外,其他因爲基本上已按本身的理解做了大量改寫,所以沒有再一一予以說明,在此對原文做者表示歉意並感謝。
另外,文中圖片小部分來自網絡,大部分爲本人制做,也再也不一一說明,在此對原圖做者表示歉意並感謝。
(未完待續)