正則表達(dá)式完全學(xué)習(xí)手冊(cè):菜鳥入門指導(dǎo)
原創(chuàng)【51CTO精選譯文】正則表達(dá)式可以很恐怖,真得很恐怖。幸運(yùn)的是,一旦記住每個(gè)符號(hào)所表達(dá)的意思,恐懼就會(huì)快速消退。如果你對(duì)正則表達(dá)式一無所知,正如文章標(biāo)題,那你又就有很多東西要學(xué)了。下面讓我們馬上開始吧。
第一節(jié):基礎(chǔ)學(xué)習(xí)
#t#想要高效地學(xué)習(xí)和掌握正則表達(dá)式的關(guān)鍵是花一天的時(shí)間記住所有符號(hào)。這可能是我所能提供的最好的建議。坐下來,做些記憶卡片,然后記住它們。以下為最常見的一些符號(hào):
. - 匹配任意字符,換行符除外(如果 dotall 為 false)。
* - 該符號(hào)前面的字符,匹配 0 次或多次。
+ - 該符號(hào)前面的字符,匹配 1次或多次
? - 該符號(hào)前面的字符是可選的。匹配 0 次或 1 次。
\d - 匹配任何單個(gè)數(shù)字。
\w - 匹配任何一個(gè)字符(包括字母數(shù)字以及下劃線)。
[XYZ] - 匹配字符組中的任意一個(gè)字符,即 X、Y、Z 中的任意一個(gè)。
[XYZ]+ - 匹配字符組中的一個(gè)或多個(gè)字符。
$ - 匹配字符串結(jié)束的位置。
^ - 匹配字符串開始的位置。
[^a-z] - 當(dāng)出現(xiàn)在字符類中時(shí),^ 表示 NOT(非);對(duì)于該示例,表示匹配任何非小寫字母。
很悶吧,不過還是記住它們,記住之后你會(huì)知道好處的。
工具
你認(rèn)為一個(gè)表達(dá)式是正確的,非常正確,但就是無法得到想要的結(jié)果,這時(shí)你可能會(huì)產(chǎn)生將頭發(fā)拔光的沖動(dòng)。去下載 RegExr 桌面應(yīng)用程序吧,這個(gè)對(duì)你是必不可少的,而且玩起來非常有趣的。它提供實(shí)時(shí)檢查,還有一個(gè)側(cè)邊欄,里面包含了每個(gè)字符的定義和用戶,非常詳細(xì)。
第二節(jié):正則表達(dá)式傻瓜教程:抓屏視頻
下一步是學(xué)習(xí)如何真正地使用這些符號(hào)。如果視頻是你的偏好,那你走運(yùn)了。這里有五個(gè)課程的視頻教程,非常適合你:“正則表達(dá)式傻瓜教程”。
(Jeffery Way:在這一系列視頻教程中,我將交給你如何在JavaScript和PHP中高效的使用正則表達(dá)式。我會(huì)假設(shè)你是從零開始。)
第三節(jié):正則表達(dá)式和 JavaScript
本節(jié)為最后一節(jié),我們來看看JavaScript 方法如何使用正則表達(dá)式。
1. Test()
這個(gè)方法接受單個(gè)字符串參數(shù),然后返回一個(gè)布爾值,該值表明是否找到一個(gè)批評(píng)。如果你不需要對(duì)特定的匹配結(jié)果進(jìn)行操作,比如,驗(yàn)證用戶名,“test”方法已足夠完成這個(gè)任務(wù)。
示例
- var username = 'JohnSmith';
- alert(/[A-Za-z_-]+/.test(username)); // returns true
在上面的代碼中,我們首先聲明一個(gè)正則表達(dá)式,表示僅允許大寫或小寫字母、下劃線和連字符。將這些可接受的字符放在括號(hào)中,就指定了一個(gè)字符組。緊隨其后的 + 號(hào)表示我們想要正在尋找的是一個(gè)或多個(gè)前述字符組中的字符。然后使用該范式對(duì)變量“JohnSmith”進(jìn)行測(cè)試。由于存在匹配,瀏覽器的顯示框中將顯示 true。
2. Split()
你可能對(duì) split 方法已經(jīng)很熟了。該方法接受單個(gè)正規(guī)表達(dá)式,表示在哪里進(jìn)行“分割”。請(qǐng)注意,如果喜歡,你還可以使用字符串。
- var str = 'this is my string';
- alert(str.split(/\s/)); // alerts "this, is, my, string"
上面代碼中的 \s 表示單個(gè)空格,通過它,我們將字符串分割為一個(gè)數(shù)組。如果想要訪問某個(gè)特定的值,使用相應(yīng)的索引即可。
- var str = 'this is my this string';
- alert(str.split(/\s/)[3]); // alerts "string"
3. replace()
可能你已經(jīng)想到了,replace 方法可以用來將文本中的一部分(由字符串或正則表達(dá)式表示)替換為不同的字符串。
示例
如果想要將“Hello, World”改為“Hello, Universe”,可以使用下面的代碼:
- var someString = 'Hello, World';
- someString = someString.replace(/World/, 'Universe');
- alert(someString); // alerts "Hello, Universe"
應(yīng)留意的是,對(duì)于這個(gè)簡單的示例,我們本來可以簡單的使用.replace('World', 'Universe')。另外,使用 replace 方法不會(huì)自動(dòng)重寫變量的值,我們必須將返回值再次分配給這個(gè)變量:someString。
示例 2
再舉一個(gè)例子,假設(shè)用戶要在我們的網(wǎng)站注冊(cè)一個(gè)賬號(hào),我們可能想要提供一些基礎(chǔ)的安全預(yù)防措施。也許我們想要留下他們的用戶名,而刪除其他任何符號(hào),引號(hào)、分號(hào)等等。對(duì)于 JavaScript 和正則表達(dá)式,執(zhí)行這類任務(wù)是樁瑣碎的小事情。
- var username = 'J;ohnSmith;@%';
- username = username.replace(/[^A-Za-z\d_-]+/, '');
- alert(username); // JohnSmith;@%
看到最后生成的顯示值,有人可能會(huì)想,上面的代碼有錯(cuò)誤。事實(shí)并非如此。你仔細(xì)看看,會(huì)發(fā)現(xiàn)字母“J”后的分號(hào)被刪除了,正如我們所期望的那樣。為了通知引擎繼續(xù)搜索字符串查找更多匹配,我們可以做結(jié)束的斜杠后面直接添加一個(gè)“g”,這個(gè)修飾符或標(biāo)記表示“global(全局)”。修改后的代碼如下所示:
- var username = 'J;ohnSmith;@%';
- username = username.replace(/[^A-Za-z\d_-]+/g, '');
- alert(username); // alerts JohnSmith
現(xiàn)在,正則表達(dá)式搜索整個(gè)字符串,替換所有必要的字符。讓我們看看關(guān)鍵的表達(dá)式(.replace(/[^A-Za-z\d_-]+/g, '');),要注意,括號(hào)內(nèi)的向上箭頭(即 ^)非常重要。當(dāng)放在字符組中時(shí),該符號(hào)表示“找到所有不是……”?,F(xiàn)在回頭在看看這個(gè)代碼,它表示,找到所有不是字母、數(shù)字(由 \d 表示)、下劃線或連字符的符號(hào);如果找到一個(gè)匹配,將其替換為空,事實(shí)上就是刪除該字符。
4. Match()
與test方法不同,match() 返回一個(gè)包含所有找到的批評(píng)的數(shù)組。
示例
- var name = 'JeffreyWay';
- alert(name.match(/e/)); // alerts "e"
上面的代碼將顯示一個(gè)字母“e”。但是,在字符串“JeffreyWay”中實(shí)際上包含2個(gè) e。同樣,這次我們還是要使用修飾符“g”來聲明一個(gè)全局搜索。
- var name = 'JeffreyWay';
- alert(name.match(/e/g)); // alerts "e,e"
如果想要顯示數(shù)組中這些特定值中的一個(gè),可以做括號(hào)中引用想要的索引。
- var name = 'JeffreyWay';
- alert(name.match(/e/g)[1]); // alerts "e"
示例 2
讓我們看下一個(gè)示例,確保我們對(duì)它的理解是正確的。
- var string = 'This is just a string with some 12345 and some !@#$ mixed in.';
- alert(string.match(/[a-z]+/gi)); // alerts "This,is,just,a,string,with,some,and,some,mixed,in"
在這個(gè)正則表達(dá)式中,我們創(chuàng)建了一個(gè)范式,可匹配一個(gè)或多個(gè)大寫或小寫字母。這多虧了“i”修飾符。除外,我們還加上了“g”來聲明進(jìn)行全局搜索。上面的代碼將顯示“This,is,just,a,string,with,some,and,some,mixed,in.”然后,如果想要獲取變量數(shù)組中這些值中的某一個(gè),我們只需引用相應(yīng)的索引即可。
- var string = 'This is just a string with some 12345 and some !@#$ mixed in.';
- var matches = string.match(/[a-z]+/gi);
- alert(matches[2]); // alerts "just"
分割電子郵件地址
為了練習(xí),我們?cè)囍鴮⒁粋€(gè)電子郵件地址(nettuts@tutsplus.com)分割為相應(yīng)的兩部分:用戶名和域名,即 nettuts 和tutsplus。
- var email = 'nettuts@tutsplus.com';
- alert(email.replace(/([a-z\d_-]+)@([a-z\d_-]+)\.[a-z]{2,4}/ig, '$1, $2')); // alerts "nettuts, tutsplus"
如果對(duì)于正則表達(dá)式,你還是新手,上面的代碼可能會(huì)看起來有點(diǎn)嚇人。不用擔(dān)心,第一次看到都會(huì)感到“恐怖”。一旦將它分解為一個(gè)一個(gè)小子集,你會(huì)發(fā)覺其實(shí)非常簡單。下面讓我們一條一條進(jìn)行分析:
- .replace(/([a-z\d_-]+)
從中間開始看,我們要搜索任何字母、數(shù)字、下劃線或連字符,并且匹配一次或多次(+)。無論匹配是上面,我們想要訪問其值,所以將其放到括號(hào)中。這樣,我們稍后可以引用這個(gè)匹配的子集。
- @([a-z\d_-]+)
緊接著前一個(gè)匹配,我們看到 @ 符號(hào),然后是有一組一個(gè)或多個(gè)字母、數(shù)字、下劃線和連字符。同樣,我們將其放到括號(hào)內(nèi),以便稍后訪問。
- \.[a-z]{2,4}/ig,
繼續(xù)找,我們看到一個(gè)點(diǎn)。因?yàn)樵谡齽t表達(dá)式中,句點(diǎn)可表示任何字符(有時(shí)換行符除外),因此必須使用“\”進(jìn)行轉(zhuǎn)義。最后一部分是用于查找“.com”。我們知道,大多數(shù)域名,如果不是所有,其后綴為 2 到 4 個(gè)字符(com、edu、net、name 等等)。如果找到具體的范圍,我們可以先行使用更常規(guī)的符號(hào),如 * 或 +。不過,我們?cè)谶@里是將2個(gè)數(shù)字放到一個(gè)大括號(hào)中,分別表示最大值和最小值。
- '$1, $2')
這個(gè)最后一部分表示replace方法的第二個(gè)參數(shù),或者我們想要將匹配字符集替換為的對(duì)象。在這里,我們使用 $1 和 $2 分別來引用保存在第一個(gè)和第二個(gè)括號(hào)中的值。對(duì)于這個(gè)特定示例, $1 指向 nettuts,$2 指向 tutsplus。
創(chuàng)建自己的位置對(duì)象
作為最后的一個(gè)項(xiàng)目,我們將創(chuàng)建位置對(duì)象。位置對(duì)象向用戶提供有關(guān)當(dāng)前頁面的信息:href、協(xié)議、地址、端口等。請(qǐng)注意,這里僅僅是作為練習(xí)之用。對(duì)于真正的網(wǎng)址,使用已有的位置對(duì)象即可。
首先我們創(chuàng)建位置函數(shù),該函數(shù)接受單個(gè)參數(shù),該參數(shù)表示我們想要“解碼”的網(wǎng)址,我們將其稱為“l(fā)oc”。
- function loc(url) { }
現(xiàn)在,我們可以按照下面的方式調(diào)用它,并傳入一個(gè)亂七八糟的 url:
- var l = loc('http://www.somesite.com?somekey=somevalue&anotherkey=anothervalue#theHashGoesHere');
下一步,我們需要返回包含多個(gè)方法的對(duì)象。
- function loc(url) {
- return {
- }
- }
搜索(search)
我們不會(huì)創(chuàng)建所有方法,但我們會(huì)模仿其中幾個(gè)。第一個(gè)是“search”。使用正則表達(dá)式,我們將要搜索 url 并返回查詢字符串中的所有內(nèi)容。
- return {
- search : function() {
- return url.match(/\?(.+)/i)[1];
- // returns "somekey=somevalue&anotherkey=anothervalue#theHashGoesHere"
- }
- }
在上面的代碼中,我們使用了傳入的 url,試圖使用我們的正則表達(dá)式對(duì)其進(jìn)行匹配。這個(gè)正則表達(dá)式在整個(gè)字符串中搜索問號(hào),問號(hào)表示查詢字符串(querystring)的開始。在這個(gè)位置,我們需奧獲取其余的字符,這就是將(.+)放入括號(hào)的原因。最后,我們需要返回那個(gè)字符區(qū)塊,因此,使用[1]來定位它。
哈希值(Hash)
現(xiàn)在我們將創(chuàng)建一個(gè)方法,返回 url 的哈希值,或者 # 號(hào)后面的內(nèi)容。
- hash : function() {
- return url.match(/#(.+)/i)[1]; // returns "theHashGoesHere"
- },
這次,我們搜索 # 號(hào),同樣,使用括號(hào)獲取后面的字符,并使用 [1] 指向那個(gè)特定的子集。
協(xié)議
protocol 方法應(yīng)返回,頁面所用的協(xié)議,可能你已經(jīng)猜到了。這種協(xié)議通常為 http 或 https。
- protocol : function() {
- return url.match(/(ht|f)tps?:/i)[0]; // returns 'http:'
- },
這一個(gè)稍微更加復(fù)雜一點(diǎn),因?yàn)榇嬖趲讉€(gè)選項(xiàng):http、https 和 ftp。
雖然可以使用這樣的格式 (http|https|ftp),但使用 (ht|f)tps? 更為簡潔,表示我們首先查找“ht”或“f”字符,下一步,匹配“tp”字符。最后的“s”是可選的,所以我們加了一個(gè)問號(hào),表示問號(hào)前的字符出現(xiàn)零次或一次。
Href
這個(gè)是最后一個(gè)方法,返回頁面的 url。
- href : function() {
- return url.match(/(.+\.[a-z]{2,4})/ig); // returns "http://www.somesite.com"
- }
這里,我們對(duì)所有字符進(jìn)行匹配,直到找到一個(gè)點(diǎn)號(hào),這個(gè)點(diǎn)號(hào)后面有 2 - 4 個(gè)字符(表示 com、au、edu、name 等等)。重要的是意識(shí)到,對(duì)于這些表達(dá)式,我們可以寫得很復(fù)雜也可以寫得很簡單,這在于我們要求有多嚴(yán)格。
一個(gè)簡單的函數(shù)
- function loc(url) {
- return {
- search : function() {
- return url.match(/\?(.+)/i)[1];
- },
- hash : function() {
- return url.match(/#(.+)/i)[1];
- },
- protocol : function() {
- return url.match(/(ht|f)tps?:/)[0];
- },
- href : function() {
- return url.match(/(.+\.[a-z]{2,4})/ig);
- }
- }
- }
使用上面這個(gè)函數(shù),我們可以很簡單地顯示網(wǎng)址的每個(gè)部分:
- var l = loc('http://www.net.tutsplus.edu?key=value#hash');
- alert(l.href()); // http://www.net.tutsplus.com
- alert(l.protocol()); // http:
- ...etc.
就這樣。
原文:You Don’t Know Anything About Regular Expressions: A Complete Guide
作者:Jeffrey Way