TRY REGEX 是一個交互式的正則表達式學習項目
項目地址:https://github.com/callumacrae/tryregex
在線地址:http://tryregex.com/
翻譯版在線:http://www.rakuhi.com/tryregex-cn/
很適合正則表達式初學者學習、練習
總共28個小課程,從零基礎入門到能完成大多數正則表達式書寫。
個人做了 翻譯&解答,有問題之處歡迎指正
翻譯
第一課 正則表達式入門
本交互課程意在介紹正則表達式,更明確的說是JavaScript下的正則表達式。課程當然也會教您用其他語言寫正則表達式,但是您應該知道它們是有區別的。
左側的控制臺就是一個JavaScript控制臺。請使用 <code>setName('Your name')</code> (Your name替換為您的名字)命令設置您的姓名來開始課程。
還有一些有用的命令:執行 <code>help()</code> 來查看它們
第二課 什么是正則表達式?
正則表達式(也寫作 regex 或 regexp)是一個用來描述搜索模式的字符串——類似星號(*)文件名匹配通配符,但更加有效(當然也更加復雜)。
我們將會從一個非常基礎的例子開始,來讓您掌握 JavaScript 中語法和正則表達式的使用。
<code>bio</code>是一個不知道是否包含您名字的變量。為了看看結果,請輸入 <code>bio.match(/{{ firstEscaped }}/);</code>。
第三課 包含!
從上個例子中您可以得知一些事情。第一,用于定義正則表達式的語法:使用兩個斜杠包含表達式:
<code>/表達式/</code>
如果您將它輸入控制臺,將會返回正則表達式。
第二,您可以對字符串使用 <code>.match()</code> 方法來測試正則表達式。還有一些其他方法可以調用:您可以直接對正則表達式使用 <code>.exec()</code> 方法來對執行一個字符串。請輸入 <code>/{{ firstEscaped }}/.exec(bio)</code>。
第四課 簡單測試
<code>.exec()</code> 方法和 <code>.match()</code> 方法做的是同樣的事情,但它是對表達式調用而不是對字符串——這會很有用。
另一個可用的是 <code>.test()</code> 方法 ——可能是這些方法中最簡單的 。類似于 <code>.exec()</code> ,但返回的是布爾值。試一試吧!
提示:左側控制臺可以使用向上鍵來獲取之前輸入的表達式。
第五課 字符串替換
我們會用到的最后一個方法是 <code>.replace()</code> ,用一個字符串替換另一個字符串的方法。輸入一下內容來將 <code>bio</code> 變量中您的名字隱藏:
<code>bio.replace(/{{ firstEscaped }}/, '[redacted]')</code>
第六課 特殊字符
我們迄今為止用過的表達式都不是特別有趣,也沒有包含特殊字符。以下這些字符在正則表達式中應該被轉義:
<code>$()*+.?[^|]</code>
為了規避它們,使用反斜杠轉義,例如 <code>/what?/</code> 。
寫一個正則表達式來測試 <code>num</code> 變量中是否包含字符串“3.5”。
第七課 點運算符
是不包含的!<code>num</code> 的值是 123456,它不包含字符串“3.5”。
在正則表達式中點運算符有特殊的含義:它將匹配除了換行符以外的所有的單個字符(所以 <code>/a.c/</code> 將會匹配 "abc", "a c", "a$c" 等等)。未轉義點運算符直接使用 <code>/3.5/</code> 將可能匹配到 <code>num</code> 中字符串,這里點運算符將會匹配 4。
試試吧。
第八課 量詞
有一些“量詞”可以用來表達某個東西需要被匹配多少次。第一個量詞是問號,作用是讓前置項(一個字符或字符組)可選(即出現0次或1次)。
表達式 <code>/regexp?/</code> 將會匹配 "regex" 和 "regexp",問號讓 p(只是p)可選。
寫一個表達式同時能匹配"frontend" 和 "front-end",答案通過 <code>answer()</code> 函數傳參(例子:<code>answer(/表達式/)</code>)。
第九課 加號
我們要學習的下一個量詞是加號。它的意思是“一個或更多前置項”;<code>/Princes+/</code> 會匹配 "Princes", "Princess", "Princesssss" 等等。但不會匹配"Prince"。
您要寫的下一個表達式將會有點復雜。寫一個正則表達式,抽取出 <code>shortStory</code> 變量(可輸入 <code>shortStory</code> 查看變量內容)中左右括號間的所有內容(包含括號)。提示:需要用到剛才說過的點運算符。
第十課 星號
星號和加號類似;但是不同于“一個或更多”,星號的含義是“零個或更多的前置項”。/Princes*/
在 <code>/Princes+/</code> 匹配內容的基礎上還能匹配 "Prince" 。
重復上一個例子,用星號替換加號。抽取出 <code>shortStory</code> 變量中左右括號間的所有內容,即便括號內沒有內容。
第十一課 限制重復次數
有一個量詞可以用來限制重復次數。語法是 <code>{min,max}</code> 其中 min 是最小出現次數,max 是最大出現次數。比如 <code>/a{3,5}/</code> 會且只會匹配 "aaa", "aaaa" 和 "aaaaa"。
寫一個表達式匹配 <code>bracketNumbers</code> 中左右圓括號中間長度為5~8之間的字符。
第十二課 限制重復次數進階
為了進一步明確重復次數范圍,您可以用<code>{n}</code>指明確定的一個重復次數,其中 n 是重復次數。比如表達式 <code>a{6}</code> 只會匹配重復的六個 a。
在使用大括號時,您可以不指定最大重復個數,只留下最小重復個數。比如,<code>/a{5,}/</code> 會匹配5個或更多次重復的字母 a。
用 <code>answer()</code> 函數給出不使用 ?*+
字符,但和 /a?b+c*/
等價的正則表達式。
第十三課 標志——不區分大小寫標志
標志用來改變正則表達式的表達形式,位于表達式的后面(例子:<code>/表達式/ig</code>)。每個標志用一個字母表示,JavaScript 支持四種標志——有兩種將會在本課程中學到。<code>i</code> 標志讓表達式不區分大小寫——在沒有這個標志時 <code>/a/</code> 只會匹配“a”,不會匹配“A”;<code>/a/i</code> 則會同時匹配“a”和“A”。
執行 <code>/CAT/i.exec('Category')</code> 來看看 i 標志的作用。
第十四課 標志——全局匹配標志
第二個常用的標志是全局匹配標志,用字母 <code>g</code> 表示。 <code>/a/</code> 只會匹配字符串中第一個 a, <code>/a/g</code> 則會匹配每一個單個的字母 a。
寫一個正則表達式,來用字母 “e” 替換 <code>shortStory</code> 里所有的字母 “a”。
您可以使用字符串的 <code>.replace(expr, replace)</code> 方法來做替換。
第十五課 字符組
字符組允許您指定一組或一個范圍的字符去匹配。<code>/[aeiou]/</code> 匹配任意元音字母,<code>/[a-m]/</code> 匹配任意字母表前半部分的字母,<code>/[aeiou0-9]/</code> 匹配任意元音字母或數字。
注意在字符組中,您不需要轉義點字符,它將被直接匹配。如果您要使用連字符(-),則需要轉義。
我們定義合法的用戶名包含5到12個字母(大寫字母或小寫字母)或連字符。寫一段代碼,在 <code>username</code> 合法時返回 true。
第十六課 否定字符組
否定字符組將會匹配不在字符組內的字符。您可以在字符組前添加一個脫字號(^)來否定它。比如 <code>/[^a-m]/</code> 將匹配“z” 、“$”,但是不會匹配“c”。
弄清楚 “非 [a-m]” 和 “不在 [a-m] 范圍” 的區別很重要,<code>/c[^a]t/</code> 將匹配 “cut”,但不會匹配 “cat” 和 “ct” ——這個非常重要。
現在合法的用戶名可以包含任何非空格字符(但同樣長度在 5 到 12 之間)。寫出一個正則表達式來檢驗 <code>username</code> 合法性。
第十七課 類型字符
類型字符可以用作常見字符組的簡略表達。有6種類型字符:<code>\d</code> 匹配十進制數字(0-9),<code>\s</code> 匹配空格, <code>\w</code> 匹配單詞字符(單詞字 —— Unicode字符集、數字、下劃線)。
另外三個可用的類型字符是將上面三個改成大寫字母,就起到了否定它們的效果;比如 <code>\S</code> 匹配所有非空格字符。
寫一個正則表達式,能匹配一個后綴一個空格一串數字的單詞。用 <code>charTypeTest</code> 測試:不要使用任何文字字符。
第十八課 定位符
如果您想確保字符串以某種確定字(字符集)的開始或結束——比如,您希望包裝字符串以大寫字母開始——那么你就可以使用錨點。美元符號($)匹配字符串結尾,脫字符()匹配字符串開始。<code>/cat$/</code> 將只會匹配到 “cat” 而不匹配其他任何字符串(<code>/cat/</code> 會匹配任何包含 “cat” 的字符串)。
寫一個正則表達式來測試 <code>possibleUrl</code> 變量是否以 “http://” 或 “https://” 開始,且從頭到尾不包含任何空格。
提示:需要用到問號、否定字符集。您可能會需要兩種錨點。
第十九課 捕獲組
您可以使用圓括號來創建組,可以將多項組合起來或者保存結果以供之后引用:
<code>/"(.+)"/</code>
上面就是一個捕獲組的示例,意思是圓括號內匹配到的字符串將被保存在 <code>.match()</code> 或 <code>.exec()</code> 返回的數組中。
回頭看看之前我們用表達式 <code>/(.{5,8})/.exec(shorterStory)</code> 抓取兩個圓括號之間的數據的例子。嘗試用圓括號包住 <code>.{5,8}</code> 重新運行一下。
第二十課 非捕獲組
您可以看到現在返回數組有個兩項:第一項是全部匹配內容,第二項只包含捕獲組匹配數據。
還有一種組類型叫做非捕獲組。這種組語法上稍有不同,它不在數組內存儲直。如果不需要引用組,您可能傾向使用非捕獲組:它會保持返回數組的純凈。上一個例子中,添加 <code>?:</code> 在組的開始點運算符之前,將表達式變成非捕獲組。
第二十一課 量詞
和我們zhi'q沒有用組幾乎一樣。
非捕獲組的主要用途是給一個組賦予量詞。下面的表達式將會匹配 “I ate” 和 Carrot and I ate” 不會匹配其他內容:
<code>/^(?:{{ firstEscaped }} and )?I ate$/</code>
寫一個表達式,能夠匹配 “ha” 出現兩次或更多的字符串 (例如:“haha” 或 “hahahahaha”),用 <code>answer()</code> 函數給出答案。
提示:您的表達式不應該匹配 “hahah”。使用錨點來保證這一點。
第二十二課 管道符號
您可以使用管道符號(|)來指定“或”關系。下面的表達式將匹配 “The dog ate” 和 “The cat ate”:
<code>/The (dog|cat) ate/</code>
您也可以使用非捕獲組,但是在這個例子中我們希望獲取結果。您可以在一個組內用任意多個管道符號。讓上面的表達式在保持原有匹配的基礎上也能夠匹配 “The rabbit ate” (帶匹配串在 <code>rabbit</code> 變量中)。
第二十三課 反向引用
在同一個表達式中,您可以引用之前捕獲到的值。只需簡單的寫成反斜杠后跟著捕獲組數字(它在返回字符串里的索引值)。比如,下面的表達式將匹配 “The cat ate with the other cat” 和 “The dog ate with the other dog”,但不會匹配 “The cat ate with the other dog”(當然,這本身就挺奇怪的):
<code>/The (dog|cat) ate with the other \1/</code>
寫一個表達式,能夠匹配同一行內兩個相同的單詞(比如:“hello hello world”):和之前的例子一樣,用 <code>answer()</code> 函數給出答案。
第二十四課 RegExp對象
除了文字運算符(斜杠),JavaScript 還提供 RegExp 構造器,允許您使用字符串去指定需要的表達式。這個對于將變量放在表達式中非常有用。它的使用方法如下:
// Same as /regexp?/ig
new RegExp('regexp?', 'ig');
用戶名同樣包含在變量中。 <code>userData</code> 變量包含用戶信息:把它打印在控制臺中來查看數據格式。使用 <code>username</code> 變量去抽取該用戶的關聯詞。為了能夠正確驗證,請把答案寫在同一行。
第二十五課 高級替換
我們已經知道了兩種捕獲組使用捕獲值得方法:第一種是返回數組,第二種是反向引用。您也可以在 <code>.replace()</code> 方法的第二個參數中獲取它:
var text = '*italic text*';
var replace = '<em>$1</em>';
text.replace(/\*([^*]+)\*/, replace);
寫一個類似上面的代碼,將 <code>boldText</code> 變量替換成 <code><strong></code> 元素。
第二十六課 懶惰 vs 貪婪 匹配
JavaScript 默認的匹配模式是 “貪婪”,也就是說匹配盡可能多個:
<code>'"Hi", "Hello"'.match(/".+"/)</code>
上面的表達式將會返回 <code>"Hi", "Hello"</code> ,它匹配了兩個雙引號間內容。懶惰匹配則和貪婪匹配相反,它會匹配盡可能少的——所以這個例子中,只會匹配 <code>"Hi"</code>。
通過在量詞后面加上問號來實現懶惰匹配——用上面的例子試試吧。
第二十七課 斷言
斷言是一個應該被匹配但不會被存儲的模式:不是 “匹配a然后匹配b” ,而是 “匹配后面連著b的a,但是不匹配b”。在 JavaScript 中有兩種斷言類型,肯定先行斷言 和 否定先行斷言。“先行” 就是說向前查找;JavaScript 不支持 “后行”(向后查找)。
肯定先行斷言表示我們想向前查找a的匹配。為了查找a b連接,我們可以使用 <code>/a(?=b)/</code>。
使用斷言來從 <code>partialSums</code> 中取出 “6+3”。不要使用任何數字符號,用 <code>\d</code>。
第二十八課 否定斷言
斷言也可以是否定的,所以我們可以匹配不連接在某項后的字符串。注意和字符組不同的是,它可以匹配到一些不一樣的——如果你說 “后面不連著b的a”,a可以在字符串的末尾。
否定斷言的語法和肯定斷言的語法類似,但用感嘆號(!)替換等號:比如,<code>/a(?!b)/</code> 會匹配后面不是字母 b 的字母 a。
用一個肯定斷言接一個否定斷言來提取 <code>partialSums</code> 中的 “3+3”。
您完成了課程!
恭喜您,完成了 Try Regex 的課程。您基本上掌握了 JavaScript 中正則表達式的大部分內容,現在您可以寫出適用于大多數場景的正則表達式了。
下面這些是延伸閱讀內容(不可用鏈接已替換):
- Mozilla Developer Network: Regular Expressions
- JavaScript Regular Expression Enlightenment
- DZone: The Essential Regular Expressions Cheat Sheet
- RegExp playground
- regular-expressions.info 通用非指定語言手冊
解答
正則表達式的有趣之處就在于,對于同一個問題于有多種不同的寫法。下面給出的解答有些只是正確寫法中一種,僅供參考。自己思考自己嘗試才能真正掌握正則表達式。