你是如何學會正則表達式的?


贊同 @鄭海波 ,除了自己實現一遍,剩下的也就是『無他,唯手熟爾』

另外工具方面推薦一個將 JS 正則可視化的工具:https://regexper.com/

對於理解別人源碼里的(或者自己的,哈哈)複雜正則很有幫助:


自詡精通正則... 搶答了

我簡單回憶了下,其實很簡單,就是一點

所有正則表達式的需求,都應該經過自己思考完成. 你每次『ctrl+c ctrl+v』其實都丟失了一次獨立思考的機會,即便你事後已經思考了為什麼這個正則行得通。

我完全反對,上面任何關於正則表達在幾小時內就能掌握的論調,雖然我在學校時也是從一個類似30Minute掌握正則表達式的教程開始的。這東西一定要持續的用,才能形成穩定的記憶,畢竟正則這個東西根本不是設計用來給人閱讀的。。。

工具

其實沒那麼麻煩了,Sublime Text 或 類似編輯器的 『ctrl + F』 足矣,平時開發順帶練了.

記得打開左下的正則開關

其實大部分涉及到字元串模式匹配的都可以用正則解決,我大概羅列下自己的經歷

1. 入門: 解決各個業務上的問題,比如郵箱、手機號等等,開始知道正則能解決什麼問題,以及正則的受限性。
2. 進階: 比如在最初寫GitHub - leeluolee/puer: more than a live-reload server, built for efficient front-end development的時候, 嘗試實現一個簡化的router。 在這個過程中,開始會使用子匹配和特定的宿主語言API實現一些高級特性,類express的route 會支持 括弧內引入子規則 ,比如 /api/blogs/:id(d+) ,這裡會有一些較複雜遞歸的處理,配合String.prototype.replace(首參數支持正則)可以較好的解決。在這個階段,基本我已經可以感覺已經無需查閱任何關於正則的資料,可以自行解決不太複雜的文本模式匹配問題,初步形成了記憶。

3. 再進階: 碰到更複雜的正則表達式的需求 我嘗試了使用javascript生成正則表達式,比如在GitHub - leeluolee/nes: a small js selector lib with incredible extensibility, but still very fast 拼裝了一個下面的正則,這個幾乎是無法人肉維護了,必須用代碼生成。

/(s*,s*)|(#([wu4e00-u9fbf-]+))|(*|w+)|(.([wu4e00-u9fbf-]+))| (:([wu4e00-u9fbf-]+)(?:(([^()]*|(?:([^)]+)|[^()]*)+)))?)| ([([wu4e00-u9fbf-]+)(?:([*^$|~!]?=)[""]?((?:[wu4e00-u9fbf-]||s)+)[""]?)?])|(::([wu4e00-u9fbf-]+)) |([&>s+~%](?!=))|(s*{s*(d*),(-?d*)s*}s*)/g

後面的一些非業務項目,涉及到的更複雜場景,基本也是類似的解決方案,只不過生成的正則更加噁心。我可以列舉下,比如

  • https://github.com/leeluolee/mcss/blob/master/lib/tokenizer.js#L199
  • https://github.com/regularjs/regular/blob/master/src/parser/Lexer.js#L225

後面碰到的這些問題,其實有些不算是完全的正則了,因為會與上下文相關,到這裡基本上你可以有十足的把握清楚何時才去使用正則表達式。


當然和 @vczh 一樣有精力的話,去實現一個正則引擎,肯定會是一勞永逸的記憶方式。


  • 把正則表達式必備網站RegExr: Learn, Build, Test RegEx加入書籤欄,這個網站非常強大,主要的功能有:

a.公式上的懸浮氣泡UI解釋該規則匹配的字元串

b.匹配結果也有對應的解釋

c.右側邊欄有正則表達式的詳細規範及圖中的小抄

  • 再配合刷幾道HackerRank上正則表達式的題Programming Problems and Competitions :: HackerRank。

這樣最多幾個小時,正則表達式的基本使用就沒問題了。工作中遇到的正則表達式問題基本上都可以用RegExr網站幫助解決。


Why Regular Expression

我們先來看看,我們干哈要學正則表達式這玩意兒:

  • 複雜的字元串搜尋、替換工作,無法用簡單的方式(類似藉助標準庫函數)達成。
  • 能夠幫助你進行各種字元串驗證。
  • 不止應用於編程語言中:JavaScript、JAVA、Perl、PHP、C#...
  • 也應用於許多操作系統的主流指令中:Linux/Unix、Mac、Windows PowerScript

在我們常用的開發工具中,如Fiddler Willow、WebStorm、Vim,正則表達式也能幫助我們方便的進行FindReplace的工作。由於正則表達式的流派很多,這篇文章主要是描述JavaScript中的正則表達式。

原文鏈接:玩轉JavaScript正則表達式 - 騰雲閣


以下是正文:

介紹點語法

定義

所謂正則表達式,就是一種描述字元串結構模式的形式化表達方法。

這是《精通正則表達式》對於它的定義,反正我看了這句話還是不知道正則表達式是幹嘛用的,不過沒關係,下面我們先來看一下JavaScript的正則表達式中一些常用的語法。

創建方式

在JavaScript中,我們可以通過RegExp()構造函數或者RegExp直接量兩種方式去創建正則表達式。

var pattern1 = /s$/;
var pattern2 = new RegExp("s$");

上面代碼中的pattern1和pattern2是等價的,都是用來匹配所有以字母s結尾的字元串。

多說兩句:

在創建變數時,對於布爾、數值、字元串、null和undefined這個五個原始值類型來說,原始類型優於封裝對象,原因如下。

1、不同於字元串直接量,new出來的String是一個真正的對象,這意味著你不能使用內置操作符來比較兩個截然不同的String對象的內容。

var s = new String("hello");
typeof "hello"; // string
typeof s; // object

var s1 = new String("hello");
var s2 = new String("hello");
s1 === s2; // false

這是因為每個String對象對是一個單獨的對象,其總是只等於其自身。使用不嚴格相等運算符也是一樣。

s1 == s2; // false

2、在ES5規範中,就像[],{}這樣的對象直接量一樣,程序運行時每次碰到RegExp直接量都會創建新對象。比如,如果在循環體中寫var pattern = /s$/,則每次遍歷都會創建一個新的正則表達式對象。然而在ES3規範中一個正則表達式直接量會在執行到它時轉換為一個RegExp對象,同一段代碼的正則表達式直接量的每次運算都返回同一個對象。而ES5做了相反的規定。用下面這段代碼做比較。

function getRE() {
var re = /[a-z]/;
re.foo = "bar";
return re;
}

var reg = getRE();
re2 = getRE();
console.log(reg === re2); // 在ES3中返回true,在ES5中返回false
reg.foo = "baz";
console.log(re2.foo); // 在ES3中返回"baz",在ES5中返回"bar"

顯然ES5的規範更符合開發者的期望。

各種表格(圖太多,請大家直接收藏原文吧~ 玩轉JavaScript正則表達式 - 騰雲閣

(⊙o⊙)

一些栗子

匹配URL

常見的URL:http://hostname/path.html當然,.htm或.shtml的結尾也很常見,或者乾脆沒有path部分,還包括http或https的協議頭。

  1. 其實hostname的規則比較複雜,但是跟在http(s)://之後的就有可能是主機名,所以這個部分先簡單的用[-a-z0-9_.]來匹配,再加上可能存在的埠號,所以再加上:, 就成了[-a-z0-9_.:]。
  2. path部分變化更多,所以需要使用[-a-z0-9_:@?=+,.!/~*%$]來匹配。注意,連字元必須放在字元組的開頭,保證它是一個普通字元,而不是用來表示範圍。
  3. 綜合起來,我們得到的正則表達式就是:var patternURL = /https?://[a-z0-9_.:]+/[-a-z0-9_:@?=+,.!/~*%$]*(.(html|htm|shtml))?/
  4. 因為我們降低了對匹配的要求,所以"http://.../foo.html" 這種顯然不是合法URL的字元串也能匹配,不過我覺得還好,畢竟我們需要在正則匹配的複雜性和完整性之間取得平衡。

接下來,我們一步步地對URL進行分析。

我們可以將URL分為三個部分:

  1. 協議頭:^http://或^https://
  2. 主機名:主機名是位於^http://之後和第一個反斜桿(如果有的話)之前的內容。
  3. 路徑:除了上面兩者之外的內容。
  4. 得到正則表達式:var patternURL = /^https?://([^/]+)(/.*)?$/
  5. 由於URL可能包含埠號,它位於主機名和路徑之間,以冒號開頭: (:(d)+)?
  6. 得到正則表達式:var patternURL = /^https?://([^/:]+)(:(d)+)?(/.*)?$/
  7. 匹配合法的主機名:由點號分隔部分組成,每個部分可以包括ASCⅡ字元、數字和連字元,但不能以連字元開頭和結尾。則可以得到:var patternHostname = /[a-z0-9]|[a-z0-9][-a-z0-9]*[a-z0-9]/i
  8. 結尾的後綴部分只有有限個可能:(com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|[a-z][a-z])
  9. 完善後得到:var patternHostname =/^([a-z0-9].|[a-z0-9][-a-z0-9]{0,61}[a-z0-9].)(com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|[a-z][a-z])$/i

匹配HTML Tag

匹配HTML標籤嘛,感覺很簡單的樣子,我們的第一反應可能是:var pattern = /&<[^&>]+&>/
不過這樣匹配可能存在的問題是:如果tag中含有&>,上面的正則就不能正常匹配了。如:

&


雖然上面這種HTML的寫法很少(sha)見(bi),但確實合法的。因此,簡單的&<[^&>]+&>就不能用了,需要想個聰明點的辦法。

我們先來看一下HTML Tag中有什麼規則:&<...&>中能夠出現

  1. 引用文本(被單引號或雙引號包裹的)
  2. 非引用形式的「其他文本」(包括除了&>和引號之外的任何字元)

引用文本:HTML中的引文可以用雙引號,也可以用單引號,但不允許嵌套轉義的引號。

因此我們可以使用/("[^"]*"|"[^"]*")/來匹配。

其他文本:除了&>和引號之外的任意字元

可以使用/[^"">]/來匹配
現在可以得出匹配HTML Tag的正則表達式最終版

var pattern = /&<("[^"]*"|"[^"]*"|[^"">])*&>/

給這個正則表達式來點注釋:

&< # 開始的尖括弧"&<" ( # 任意數量的... "[^"]*" # 雙引號字元串 | # 或者是... "[^"]*" # 單引號字元串 | # 或者是... [^"">] # "其他文本"
)* #
&> # 結束的尖括弧">"

需要注意的是,我們不用"+"來修飾[^"">]的原因是([^"">]+)*可能會帶來災難性的後果。匹配次數呈指數級增長。比如:對於簡單的目標字元串helloworld,是星號會迭代10次,每一次迭代中[^"">]+匹配一個字元?還是星號迭代3次,內部的[^"">]+分別匹配5、2、3個字元?或者2、3、1、4個字元?還是其他情況?這樣會把正則引擎搞瘋掉的啦!

匹配String

其實匹配引號內字元串的最簡單辦法是用這個表達式:/"[^"]*"/。

不過我們要容許其中包含轉義的引號,例如:"we have a "awesome" world!"。

下面進行任務分解:

  1. 匹配起始引號
  2. 匹配正文
  3. 匹配結束引號

不過由於轉義之後的引號也能夠出現的正文中,所以處理起來比較棘手哈。

我們還是以"we have a "awesome" world!"為例子。如果JavaScript中有逆序環視(lookaround)可用,我們可以這樣寫:var pattern = /"([^"]|(?&<=\)")*"/。

但是這個正則表達式無法匹配下面這兩個無聊的例子:"/-|-" or "[^-^]"
我本來想匹配"/-|-",結果匹配的確是"/-|-" or "。

註:

這裡的結束分隔符是一個引號,但正文也可能包含轉義之後的引號。匹配開始和結束分隔符很容易,訣竅就在於,匹配正文的時候不要超越結束分隔符。

匹配正文的思路:1、不是引號:由[^"]匹配。2、是一個引號,而它左邊又有一個反斜桿,那麼這個引號也屬於正文。使用逆序環視:/"([^"]|(?&<=\)")*"/

鑒於上面的例子,我們需要對var pattern = /"([^"]|(?&<=\)")*"/進行修改!

第一個表達式的問題在於,我們把反斜桿認為只是用來轉義引號的,其實反斜桿在字元串中可以用來轉義任何字元。因此,我們要匹配的文本其實是開始引號和結束引號之間,包括轉義字元和非引號的任何字元。得到:/"(\.|[^"])*"/

不過!

上面的表達式還是會錯誤的匹配:"You need a new"world" haha. 中的"You need a new"world" 即使這並不是一個字元串。

因為,這個表達式一開始匹配到了引號之後的文本,如果找不到結束的引號,它就會回溯。而[^"]匹配到了world里的反斜桿後,之後的那個引號會被表達式認為是一個結束的引號。。。

繼續改改改!

所以我們需要保證,字元串里的反斜桿不能以[^"]方式匹配。要將[^"]改為[^"]
上面的正則表達式使用了JavaScript正則表達式並不茲瓷的逆序環視,這裡給出JavaScript支持的版本。

/([""])(((\.|[^1\])*)+)1/ 或者 /^([""])(((\[""])?([^1])*)+)1/

好了,由於本人筆力有限,關於JavaScript的正則表達式只能介紹到這裡,感興趣的同學可以去閱讀犀牛書的第十章以及《精通正則表達式》這本書


大學,在學校團隊里,做一個網站,叫做海投網(海投網 - 大學生求職搜索引擎),每天對著各種奇形怪狀的網頁源碼,寫正則,提取招聘信息,然後做聚合,整整做了一個學期,當時遇到了各種奇葩的頁面結構,每次為了一個正則都要絞盡腦汁大半天。

正則能力不是看出來的,也不是學出來的,真的是一次又一次遇到頭疼的問題,動手寫出來的。

我經常練習正則,比如遍歷時做文件匹配,過濾 `.js` 文件但保留 `.min.js` 文件,通常就會打開 Chrome 瀏覽器,在控制台寫起來;比如處理文件格式,將 less 文件轉換為 css 文件,會嘗試使用正則將層疊括弧去掉,將 less 文件扁平化;再比如使用正則動手寫一個 js 文件的語法高亮,寫一個 ToDone 的語法高亮等等。

也經常會記錄一些正則學習的經驗,剛找了下,以前寫過的兩篇文章:進階正則表達式,玩轉正則之highlight高亮。

多練習,多找些問題刁難自己,就會有提高了~


初次學習正則語法是看了這篇文章 https://deerchao.net/tutorials/regex/regex.htm , 這篇文章至今在百度搜正則表達式入門都排在第一位, 仍然是正則新手入門非常推薦的一篇文章。

但是注意這只是學習正則語法,千萬不要以為看了一遍你就會用了,想要熟練掌握,就:

一定要多練

一定要多練

一定要多練

正則的學習是個孰能生巧的過程,用多了自然就把常用的用法記住了,但是那些符號我覺得沒有刻意記得必要,用的時候查一下就OK了。

正則表達式雖然很小,但是真的很精巧,碼蜂社《Web突破班》的同學前幾節課的內容中就包含了正則表達式,因為確實是個能有效提升工作生活效率和體驗的好工具呀,性價比絕對高~大學時期的編譯原理老師說,有次他老婆的老闆給了她一個任務,從一個很大的Excel文件裡頭整理客戶的電話,評估的工作量是一周,結果他寫了個正則一下子給全找出來了,把他老婆給樂的呀,當然他老婆還是完美地把任務拖了一周,其實一周都是用來約閨蜜逛街喝茶打麻將了~

對了,推薦個網站 Regexper 把正則表達式全部轉換成狀態機,很形象,初學者可以一看。雖然我學的時候沒看~


以前學正則表達式老是記不住,最近跟著輪子哥@vczh的教程,剛寫完一個正則表達式引擎,現在想忘也忘不了


有本書叫做《精通正則表達式》


懶得寫暴力代碼


真正入門,看的這個:4G Spaces - 編程珠璣番外篇-C.正則表達式精義-1

文章不長,雖然沒有面面俱到,但本質講得很清楚,後面學起來就沒有心理負擔了。


有本書, 叫做精通正則表達式...
然而如果你只是學來做正則匹配和正則替換的話, 看到59頁就夠了... 沒記錯的話, 59頁講的是lookahead, 學到這裡, 日常使用基本就夠了...
你可能還需要根據你使用的語言學一下語言特定語法, 不過一般十幾分鐘看一遍就好了.


百度學會的。
順便思考了一下,如果我來設計正則表達式,我應該怎麼做。


在文本編輯器中搜索及替換代碼,學了某些正則語系的語法,一段時間不用又忘了。
近年寫過一個簡單的正則引擎,然後過了一段日子語法也忘了。
我覺得還是跟工作相關吧,如果要處理很多文字,用得多就會記得了。


開始寫正則,是因為要寫爬蟲抓東西。寫過爬蟲的應該都知道,儘管很多時候bs4,lxml之類的庫的可以幫助我們很多,正則還是寫爬蟲不可拋棄的東西。
之前有個朋友跟我說,會使用正則的都是真愛編程的。雖然不是很準確,但是也是一定程度激勵了我學習正則表達式。
至於怎麼學習,我覺得就是多練習(編程都是這樣吧)

第一步要做的是通讀一遍關於正則的基礎,一邊看一邊練習是一個不錯的選擇。
有關於基礎,我推薦這個文章,可以讓你在最短的時間通讀一遍正則基礎內容
正則表達式30分鐘入門教程
當然希望你能在看基礎的時候也能一邊練習,關於練習,如果你不願意在本地,你同樣可以選擇在線的正則表達式測試,推薦一下兩個網址:
正則表達式在線測試 (站長工具)
在線正則表達式測試 (開源中國)

第二步當你通讀了一遍基礎,對正則表達式有些了解的時候,這時候你可以選擇做一些複雜的練習了。
作者:路人甲
鏈接:關於正則表達式 - 路人甲的文章 - 知乎專欄

1、查找所有漢字
2、查找Email地址
3、查找網址URL
4、查找國內電話號碼
5、查找qq號碼
6、查找身份證
7、查找中國郵編編碼
8、查找中國身份證
9、查找由26個英文字母組成的字元串
10、查找由26個英文字母的大寫組成的字元串
11、查找由26個英文字母的小寫組成的字元串
12、查找由數字和26個英文字母組成的字元串
13、查找由數字、26個英文字母或者下劃線組成的字元串

請幫我設計一個網站,某些表單如下要求:
只能輸入數字
只能輸入n位的數字
只能輸入至少n位數字
只能輸入m-n位的數字
只能輸入零和非零開頭的數字
只能輸入有兩位小數的正實數
只能輸入有1-3位小數的正實數
只能輸入非零的正整數
只能輸入非零的負整數
只能輸入長度為3的字元
只能輸入由26個英文字母組成的字元串
只能輸入由26個大寫英文字母組成的字元串
只能輸入由26個小寫英文字母組成的字元串
只能輸入由數字和26個英文字母組成的字元串
只能輸入由數字、26個英文字母或者下劃線組成的字元串
驗證用戶密碼正確格式為:以字母開頭,長度在6-18之間,
只能包含字元、數字和下劃線。
驗證是否含有^%"",;=?$"等字元
只能輸入漢字
驗證Email地址
驗證InternetURL
驗證電話號碼
驗證身份證號(15位或18位數字)
驗證一年的12個月
驗證一個月的31天


當年零基礎入行, 什麼都不會, 所幸有萬能的搜索引擎(那時候谷歌還沒被牆在外面),碰到問題都可以在上面找到答案。 然而, 當碰到正則匹配字元串相關的問題時, 搜索引擎就很難幫的上忙了,因為在大多數時候, 每一個對字元串的模式匹配都是獨一無二的特列, 在網上根本找不到案例,此外,有時候匹配的模式甚至難以用語言流暢的表達出來,靠搜索引擎搜出來的答案更是牛頭不對馬嘴了。

所幸, 那時候有個論壇叫CSDN, 火的不行。 既然搜索引擎起不了作用,只能去論壇上懸賞找人來的解決問題了。當然,CSDN上的懸賞獎勵不是RMB, 而是論壇積分, 作用么就跟現在知乎上的贊同數差不多。

CSDN上的正則帖子是最搶手的,帖子一發, 沙發、板凳、地板搶著有人坐。 而且正則相關的問題往往是一答一個準,只要手速夠快, 搶到沙發, 那麼懸賞的積分基本上最沒別人的份了。因為正則相關的問題範圍都比較狹窄,只要有示例字元串和明確的匹配需求, 那對於掌握正則的人解決起來是非常輕鬆的。而其它的問題, 往往由於提問者沒表達清楚或者牽扯到的內容太泛而難以回答,獲得積分相對來說也不容易 。

在CSDN上正則問題回答的搶眼的同學會被冠名「正則帝」,多麼狂拽炫酷吊炸天的稱號。 Js、SQL、.Net問題解決的多的人最多被稱一聲大牛, 而正則問題解決的多的則被稱為帝, 明顯不是一個level的。 而且正則表達式對於不懂的人就如無字天書一般, 這也無形中提升了掌握正則的人的逼格的層次。

因此, 當我一次次在論壇上發正則帖子求解決方案, 看著樓下的「正則帝」們寫出一個又一個能解決我的問題但如同天書般的正則表達式, 我就默默的發誓, 我也要當一次正則帝。

顯然 , 光靠網上看一些正則的教程, 是根本無法全面的掌握正則技術的。 正則是一門龐大且複雜的技術, 不經過系統的學習根本無法一覽其全貌。 我在網上買了一本《精通正則表達式》。 那時候正好是年底,公司放假,大家回家過春節。 趁那個星期放假的時間,我讀完了這本書, 就這一本不薄不厚的差不多四五百頁的書, 把正則表達式技術的來龍去脈事無巨細的講了個清清楚楚。通過它,我對正則的一切都已瞭然於胸, 我的大腦完全可以模擬出正則的匹配過程, 都是敗這本書所賜。 真的如同武俠小說中秘籍一般, 修鍊之後讓人脫胎換骨。

春節期間的修練出關以後, 接下來就要開始創盪江湖歷練展示自己技能的威力了。在之後的幾個月,我每天刷CSDN論壇列表, 尋找正則問題來練手, 日復一日, 正則水平也與日俱增。那段時間我在論壇上還打出了一定的知名度, 有的csdn網友發帖問正則問題時,還專門點我的名字求解決。 csdn上我在那個領域的地位, 甚至和現在知乎上某些領域的大牛們的聲望不相上下。

我總結了一下, 半年不到的時間, 我從一個正則門外漢逆襲成了「正則帝」原因為無非就是看書, 加上不斷的練習。對於學習正則來說, 看書是最容易不過了, 因為一本《精通正則表達式》已經對正則講的很透徹了。試問, 現在行業內, 有哪一門應用廣泛的流行技術是只用一本書就可以講明白的。 至於練習的話, 光靠工作中碰到的問題拿來練手是遠遠不夠的, 平時寫代碼時能碰到幾個正則問題, 寫一年代碼碰到需要正則來解決的問題估計用兩隻手就能數的過來。 因此, 不管用什麼方法, 需要想辦法找案例來練手。我的做法是去CSDN回答問題, 順便刷刷積分找找存在感,這樣更易堅持而且過程也不枯燥。 現在CSDN也還開著, 有興趣的同學也可以去嘗試下。


用vim


有本書叫精通正則表達式


當你需要用到的時候你去找資料仔細看看,再練習下,所謂有壓力就有動力。深入學習的話建議看看那本《精通正則表達式》。


有門課叫編譯原理


相應語言的教學手冊加上合適的工具軟體即可。

通用:Debuggex、RegexBuddy、RegExr
JavaScript:Regulex、Regexper、JRX
Perl:Regex Coach
Ruby:Rubular
Python:Pythex、Pyreb、Kodos


推薦閱讀:

如何評價螞蟻金服新一代數據可視化引擎 G2 ?
有什麼工具可以下載整個網站的內容嗎?
瀏覽器向同一域名同時發送兩個 HTTP (ajax)請求,究竟是共用了一個 TCP 連接還是兩個?
單頁應用如何解決 SEO ?
在做 iOS 和 Android 的 HTML5 開發時,你都掉到過哪些坑裡?

TAG:JavaScript | Java | 正則表達式 | Perl | CC |