正則表達式捆蜀,又稱正規(guī)表示法、常規(guī)表示法(英語:Regular Expression轰传,在代碼中常簡寫為regex驴党、regexp或RE),計算機科學(xué)的一個概念获茬。正則表達式使用單個字符串來描述港庄、匹配一系列符合某個句法規(guī)則的字符串倔既。在很多文本編輯器里,正則表達式通常被用來檢索鹏氧、替換那些符合某個模式的文本渤涌。
基礎(chǔ)語法
------------------------------------------------------------------------------
字符 | 說明
------------------------------------------------------------------------------
\ | 將下一字符標(biāo)記為特殊字符、文本把还、反向引用或八進制轉(zhuǎn)義符
------------------------------------------------------------------------------
^ | 匹配輸入字符串開始的位置
------------------------------------------------------------------------------
$ | 匹配輸入字符串結(jié)束的位置
------------------------------------------------------------------------------
* | 零次或多次匹配前面的字符或子表達式
------------------------------------------------------------------------------
+ | 一次或多次匹配前面的字符或子表達式
------------------------------------------------------------------------------
? | 零次或一次匹配前面的字符或子表達式
------------------------------------------------------------------------------
{n} | n 是非負(fù)整數(shù)实蓬。正好匹配 n 次
------------------------------------------------------------------------------
{n,} | 至少匹配 n 次
------------------------------------------------------------------------------
{n,m} | 匹配至少 n 次,至多 m 次
------------------------------------------------------------------------------
? | 當(dāng)此字符緊隨任何其他限定符(*笨篷、+瞳秽、?、{n}率翅、{n,}练俐、{n,m})
之后時,匹配模式是"非貪心的"冕臭。"非貪心的"模式匹配搜索到的腺晾、
盡可能短的字符串,而默認(rèn)的"貪心的"模式匹配搜索到的辜贵、盡可能長的字符串
------------------------------------------------------------------------------
. | 匹配除"\r\n"之外的任何單個字符
------------------------------------------------------------------------------
(pattern) | 匹配 pattern 并捕獲該匹配的子表達式
------------------------------------------------------------------------------
(?:pattern) | 匹配 pattern 但不捕獲該匹配的子表達式悯蝉,即它是一個非捕獲匹配,
不存儲供以后使用的匹配
------------------------------------------------------------------------------
(?=pattern) | 執(zhí)行正向預(yù)測先行搜索的子表達式托慨,該表達式匹配處于匹配
pattern 的字符串的起始點的字符串鼻由。它是一個非捕獲匹配,即不能捕獲供以后使用的匹配
------------------------------------------------------------------------------
(?!pattern) | 執(zhí)行反向預(yù)測先行搜索的子表達式厚棵,該表達式匹配不處于匹配
pattern 的字符串的起始點的搜索字符串蕉世。它是一個非捕獲匹配,即不能捕獲供以后使用的匹配
------------------------------------------------------------------------------
x|y | 匹配 x 或 y
------------------------------------------------------------------------------
[xyz] | 字符集婆硬。匹配包含的任一字符
------------------------------------------------------------------------------
[^xyz] | 反向字符集狠轻。匹配未包含的任何字符
------------------------------------------------------------------------------
[a-z] | 字符范圍。匹配指定范圍內(nèi)的任何字符
------------------------------------------------------------------------------
[^a-z] | 反向范圍字符彬犯。匹配不在指定的范圍內(nèi)的任何字符
------------------------------------------------------------------------------
\b | 匹配一個字邊界向楼,即字與空格間的位置。
------------------------------------------------------------------------------
\B | 非字邊界匹配谐区。
------------------------------------------------------------------------------
\cx | 匹配 x 指示的控制字符湖蜕。例如,\cM 匹配 Control-M 或回車符宋列。
x 的值必須在 A-Z 或 a-z 之間
------------------------------------------------------------------------------
\d | 數(shù)字字符匹配重荠。等效于 [0-9]。
------------------------------------------------------------------------------
\D | 非數(shù)字字符匹配。等效于 [^0-9]戈鲁。
------------------------------------------------------------------------------
\f | 換頁符匹配仇参。等效于 \x0c 和 \cL。
------------------------------------------------------------------------------
\n | 換行符匹配婆殿。等效于 \x0a 和 \cJ诈乒。
------------------------------------------------------------------------------
\r | 匹配一個回車符。等效于 \x0d 和 \cM婆芦。
------------------------------------------------------------------------------
\s | 匹配任何空白字符怕磨,包括空格、制表符消约、換頁符等肠鲫。
------------------------------------------------------------------------------
\S | 匹配任何非空白字符。與 [^ \f\n\r\t\v] 等效或粮。
------------------------------------------------------------------------------
\t | 制表符匹配导饲。與 \x09 和 \cI 等效。
------------------------------------------------------------------------------
\v | 垂直制表符匹配氯材。與 \x0b 和 \cK 等效渣锦。
------------------------------------------------------------------------------
\w | 匹配任何字類字符,包括下劃線氢哮。與"[A-Za-z0-9_]"等效
------------------------------------------------------------------------------
\W | 與任何非單詞字符匹配袋毙。與"[^A-Za-z0-9_]"等效。
------------------------------------------------------------------------------
\xn | 匹配 n冗尤,此處的 n 是一個十六進制轉(zhuǎn)義碼听盖。
十六進制轉(zhuǎn)義碼必須正好是兩位數(shù)長
------------------------------------------------------------------------------
\num | 匹配 num,此處的 num 是一個正整數(shù)裂七。到捕獲匹配的反向引用
------------------------------------------------------------------------------
\n | 標(biāo)識一個八進制轉(zhuǎn)義碼或反向引用皆看。如果 \n 前面至少有 n 個
捕獲子表達式,那么 n 是反向引用碍讯。否則,如果 n 是八進制數(shù) (0-7)扯躺,那么 n 是八進制轉(zhuǎn)義碼捉兴。
------------------------------------------------------------------------------
\nm | 標(biāo)識一個八進制轉(zhuǎn)義碼或反向引用。
------------------------------------------------------------------------------
\nml | 當(dāng) n 是八進制數(shù) (0-3)录语,m 和 l 是八進制數(shù) (0-7) 時倍啥,
匹配八進制轉(zhuǎn)義碼 nml。
------------------------------------------------------------------------------
\un | 匹配 n澎埠,其中 n 是以四位十六進制數(shù)表示的 Unicode 字符虽缕。
例如,\u00A9 匹配版權(quán)符號 (?)蒲稳。
------------------------------------------------------------------------------
常用正則表達式
用戶名:/^[a-z0-9_-]{3,16}$/
密碼:/^[a-z0-9_-]{6,18}$/
十六進制值:/^#?([a-f0-9]{6}|[a-f0-9]{3})$/
電子郵箱:/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
URL:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
IP 地址:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
HTML 標(biāo)簽:/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/
Unicode編碼中的漢字范圍:/^[u4e00-u9fa5],{0,}$/
匹配中文字符的正則表達式: [\u4e00-\u9fa5]
評注:匹配中文還真是個頭疼的事氮趋,有了這個表達式就好辦了
匹配雙字節(jié)字符(包括漢字在內(nèi)):[^\x00-\xff]
評注:可以用來計算字符串的長度(一個雙字節(jié)字符長度計2伍派,ASCII字符計1)
匹配空白行的正則表達式:\n\s*\r
評注:可以用來刪除空白行
匹配HTML標(biāo)記的正則表達式:<(\S*?)[^>]*>.*?</\1>|<.*? />
評注:網(wǎng)上流傳的版本太糟糕,上面這個也僅僅能匹配部分剩胁,對于復(fù)雜的嵌套標(biāo)記依舊無能為力
匹配首尾空白字符的正則表達式:^\s*|\s*$
評注:可以用來刪除行首行尾的空白字符(包括空格诉植、制表符、換頁符等等)昵观,非常有用的表達式
匹配Email地址的正則表達式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
評注:表單驗證時很實用
匹配網(wǎng)址URL的正則表達式:[a-zA-z]+://[^\s]*
評注:網(wǎng)上流傳的版本功能很有限晾腔,上面這個基本可以滿足需求
匹配帳號是否合法(字母開頭,允許5-16字節(jié)啊犬,允許字母數(shù)字下劃線):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
評注:表單驗證時很實用
匹配國內(nèi)電話號碼:\d{3}-\d{8}|\d{4}-\d{7}
評注:匹配形式如 0511-4405222 或 021-87888822
匹配騰訊QQ號:[1-9][0-9]{4,}
評注:騰訊QQ號從10000開始
匹配中國大陸郵政編碼:[1-9]\d{5}(?!\d)
評注:中國大陸郵政編碼為6位數(shù)字
匹配身份證:\d{15}|\d{18}
評注:中國大陸的身份證為15位或18位
匹配ip地址:\d+\.\d+\.\d+\.\d+
評注:提取ip地址時有用
匹配特定數(shù)字:
^[1-9]\d*$ //匹配正整數(shù)
^-[1-9]\d*$ //匹配負(fù)整數(shù)
^-?[1-9]\d*$ //匹配整數(shù)
^[1-9]\d*|0$ //匹配非負(fù)整數(shù)(正整數(shù) + 0)
^-[1-9]\d*|0$ //匹配非正整數(shù)(負(fù)整數(shù) + 0)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ //匹配正浮點數(shù)
^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ //匹配負(fù)浮點數(shù)
^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$ //匹配浮點數(shù)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ //匹配非負(fù)浮點數(shù)(正浮點數(shù) + 0)
^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$ //匹配非正浮點數(shù)(負(fù)浮點數(shù) + 0)
評注:處理大量數(shù)據(jù)時有用灼擂,具體應(yīng)用時注意修正
匹配特定字符串:
^[A-Za-z]+$ //匹配由26個英文字母組成的字符串
^[A-Z]+$ //匹配由26個英文字母的大寫組成的字符串
^[a-z]+$ //匹配由26個英文字母的小寫組成的字符串
^[A-Za-z0-9]+$ //匹配由數(shù)字和26個英文字母組成的字符串
^\w+$ //匹配由數(shù)字、26個英文字母或者下劃線組成的字符串
分組捕獲 "()"
分組
用小括號來指定子表達式(也叫做分組)觉至,然后你就可以指定這個子表達式的重復(fù)次數(shù)了剔应,你也可以對子表達式進行其它一些操作。
默認(rèn)情況下康谆,每個分組會自動擁有一個組號领斥,規(guī)則是:從左向右,以分組的左括號為標(biāo)志沃暗,第一個出現(xiàn)的分組的組號為1月洛,第二個為2,以此類推孽锥。
示例:
(\d{1,3}.){3}\d{1,3} 是一個簡單的IP地址匹配表達式嚼黔。要理解這個表達式,請按下列順序分析它:\d{1,3}匹配1到3位的數(shù)字惜辑,(\d{1,3}.){3}匹配三位數(shù)字加上一個英文句號(這個整體也就是這個分組)重復(fù)3次唬涧,最后再加上一個一到三位的數(shù)字(\d{1,3})
反向引用
后向引用用于重復(fù)搜索前面某個分組匹配的文本,示例:
\b(\w+)\b\s+\1\b可以用來匹配重復(fù)的單詞,像go go, 或者kitty kitty盛撑。這個表達式首先是一個單詞碎节,也就是單詞開始處和結(jié)束處之間的多于一個的字母或數(shù)字(\b(\w+)\b),這個單詞會被捕獲到編號為1的分組中抵卫,然后是1個或幾個空白符(\s+)狮荔,最后是分組1中捕獲的內(nèi)容(也就是前面匹配的那個單詞)(\1)
自定義組名
你也可以自己指定子表達式的組名。要指定一個子表達式的組名介粘,請使用這樣的語法:(?<Word>\w+)(或者把尖括號換成'也行:(?'Word'\w+)),這樣就把\w+的組名指定為Word了殖氏。要反向引用這個分組捕獲的內(nèi)容,你可以使用\k<Word>,所以上一個例子也可以寫成這樣:\b(?<Word>\w+)\b\s+\k<Word>\b姻采。
常用的分組語法
分類 代碼/語法 說明
-------------------------------------------------------------------------------
捕獲 (exp) 匹配exp,并捕獲文本到自動命名的組里
-------------------------------------------------------------------------------
(?<name>exp) 匹配exp,并捕獲文本到名稱為name的組里雅采,
也可以寫成(?'name'exp)
-------------------------------------------------------------------------------
(?:exp) 匹配exp,不捕獲匹配的文本,也不給此分組分配
組號
-------------------------------------------------------------------------------
零寬斷言 (?=exp) 匹配exp前面的位置
-------------------------------------------------------------------------------
(?<=exp) 匹配exp后面的位置
-------------------------------------------------------------------------------
(?!exp) 匹配后面跟的不是exp的位置
-------------------------------------------------------------------------------
(?<!exp) 匹配前面不是exp的位置
零寬斷言
零寬斷言
(?=exp)也叫零寬度正預(yù)測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達式exp婚瓜。比如\b\w+(?=ing\b)宝鼓,匹配以ing結(jié)尾的單詞的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.時闰渔,它會匹配sing和danc席函。
(?<=exp)也叫零寬度正回顧后發(fā)斷言,它斷言自身出現(xiàn)的位置的前面能匹配表達式exp冈涧。比如(?<=\bre)\w+\b會匹配以re開頭的單詞的后半部分(除了re以外的部分)茂附,例如在查找reading a book時,它匹配ading督弓。
負(fù)向零寬斷言
\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的單詞营曼。但是如果多做測試(或者你思維足夠敏銳,直接就觀察出來了)愚隧,你會發(fā)現(xiàn)蒂阱,如果q出現(xiàn)在單詞的結(jié)尾的話,像Iraq,Benq狂塘,這個表達式就會出錯录煤。這是因為[^u]總要匹配一個字符,所以如果q是單詞的最后一個字符的話荞胡,后面的[^u]將會匹配q后面的單詞分隔符(可能是空格妈踊,或者是句號或其它的什么),后面的\w*\b將會匹配下一個單詞泪漂,于是\b\w*q[^u]\w*\b就能匹配整個Iraq fighting廊营。負(fù)向零寬斷言能解決這樣的問題,因為它只匹配一個位置萝勤,并不消費任何字符÷锻玻現(xiàn)在,我們可以這樣來解決這個問題:\b\w*q(?!u)\w*\b敌卓。
零寬度負(fù)預(yù)測先行斷言(?!exp)慎式,斷言此位置的后面不能匹配表達式exp。例如:\d{3}(?!\d)匹配三位數(shù)字趟径,而且這三位數(shù)字的后面不能是數(shù)字瘪吏;\b((?!abc)\w)+\b匹配不包含連續(xù)字符串a(chǎn)bc的單詞。
同理舵抹,我們可以用
(?<!exp),零寬度負(fù)回顧后發(fā)斷言來斷言此位置的前面不能匹配表達式exp:(?<![a-z])\d{7}匹配前面不是小寫字母的七位數(shù)字
貪婪與懶惰
當(dāng)正則表達式中包含能接受重復(fù)的限定符時肪虎,通常的行為是(在使整個表達式能得到匹配的前提下)匹配盡可能多的字符劣砍。以這個表達式為例:a.b惧蛹,它將會匹配最長的以a開始,以b結(jié)束的字符串。如果用它來搜索aabab的話香嗓,它會匹配整個字符串a(chǎn)abab迅腔。這被稱為貪婪匹配。
有時靠娱,我們更需要懶惰匹配沧烈,也就是匹配盡可能少的字符。前面給出的限定符都可以被轉(zhuǎn)化為懶惰匹配模式像云,只要在它后面加上一個問號?锌雀。這樣.?就意味著匹配任意數(shù)量的重復(fù),但是在能使整個匹配成功的前提下使用最少的重復(fù)⊙肝埽現(xiàn)在看看懶惰版的例子吧:
a.*?b匹配最短的腋逆,以a開始,以b結(jié)束的字符串侈贷。如果把它應(yīng)用于aabab的話惩歉,它會匹配aab(第一到第三個字符)和ab(第四到第五個字符)
正則表達式編寫建議
常見問題
誤匹配:指正則表達式所匹配的內(nèi)容范圍超出了所需要范圍,有些文本明明不符合要求俏蛮,但是被所寫的正則式“擊中了”撑蚌。例如,如果使用\d{11}來匹配11位的手機號搏屑,\d{11}不單能匹配正確的手機號争涌,它還會匹配98765432100這樣的明顯不是手機號的字符串。我們把這樣的匹配稱之為誤匹配睬棚。
漏匹配:指正則表達式所匹配的內(nèi)容所規(guī)定的范圍太狹窄第煮,有些文本確實是所需要的,但是所寫的正則沒有將這種情況囊括在內(nèi)抑党。例如包警,使用\d{18}來匹配18位的身份證號碼,就會漏掉結(jié)尾是字母X的情況底靠。
寫出一條正則表達式害晦,既可能只出現(xiàn)誤匹配(條件寫得極寬松,其范圍大于目標(biāo)文本)暑中,也可能只出現(xiàn)漏匹配(只描述了目標(biāo)文本中多種情況種的一種)壹瘟,還可能既有誤匹配又有漏匹配。例如鳄逾,使用\w+.com來匹配.com結(jié)尾的域名稻轨,既會誤匹配abc_.com這樣的字串(合法的域名中不含下劃線,\w包含了下劃線這種情況)雕凹,又會漏掉ab-c.com這樣的域名(合法域名中可以含中劃線殴俱,但是\w不匹配中劃線)政冻。
編寫建議
掌握語法細節(jié)。正則表達式在各種語言中线欲,其語法大致相同明场,細節(jié)各有千秋。明確所使用語言的正則的語法的細節(jié)李丰,是寫出正確苦锨、高效正則表達式的基礎(chǔ)。例如趴泌,perl中與\w等效的匹配范圍是[a-zA-Z0-9_]舟舒;perl正則式不支持肯定逆序環(huán)視中使用可變的重復(fù)(variable repetition inside lookbehind,例如(?<=.*)abc)嗜憔,但是.Net語法是支持這一特性的魏蔗;又如,JavaScript連逆序環(huán)視(Lookbehind,如(?<=ab)c)都不支持痹筛,而perl和python是支持的莺治。《精通正則表達式》第3章《正則表達式的特性和流派概覽》明確地列出了各大派系正則的異同帚稠,這篇文章也簡要地列出了幾種常用語言谣旁、工具中正則的比較。對于具體使用者而言滋早,至少應(yīng)該詳細了解正在使用的那種工作語言里正則的語法細節(jié)榄审。
先粗后精,先加后減杆麸。使用正則表達式語法對于目標(biāo)文本進行描述和界定搁进,可以像畫素描一樣,先大致勾勒出框架昔头,再逐步在局步實現(xiàn)細節(jié)饼问。仍舉剛才的手機號的例子,先界定\d{11}揭斧,總不會錯莱革;再細化為1[358]\d{9},就向前邁了一大步(至于第二位是不是3讹开、5盅视、8,這里無意深究旦万,只舉這樣一個例子闹击,說明逐步細化的過程)。這樣做的目的是先消除漏匹配(剛開始先盡可能多地匹配成艘,做加法)赏半,然后再一點一點地消除誤匹配(做減法)梅忌。這樣有先有后,在考慮時才不易出錯除破,從而向“不誤不漏”這個目標(biāo)邁進。
留有余地琼腔。所能看到的文本sample是有限的瑰枫,而待匹配檢驗的文本是海量的,暫時不可見的丹莲。對于這樣的情況光坝,在寫正則表達式時要跳出所能見到的文本的圈子,開拓思路甥材,作出“戰(zhàn)略性前瞻”盯另。例如,經(jīng)常收到這樣的垃圾短信:“發(fā)*票”洲赵、“發(fā)#漂”鸳惯。如果要寫規(guī)則屏蔽這樣煩人的垃圾短信,不但要能寫出可以匹配當(dāng)前文本的正則表達式 發(fā)*#叠萍,還要能夠想到 發(fā).(?:票|漂|飄)之類可能出現(xiàn)的“變種”芝发。這在具體的領(lǐng)域或許會有針對性的規(guī)則,不多言苛谷。這樣做的目的是消除漏匹配辅鲸,延長正則表達式的生命周期。
明確腹殿。具體說來独悴,就是謹(jǐn)慎用點號這樣的元字符,盡可能不用星號和加號這樣的任意量詞锣尉。只要能確定范圍的刻炒,例如\w,就不要用點號自沧;只要能夠預(yù)測重復(fù)次數(shù)的落蝙,就不要用任意量詞。例如暂幼,寫析取twitter消息的腳本筏勒,假設(shè)一條消息的xml正文部分結(jié)構(gòu)是<span class=”msg”>…</span>且正文中無尖括號,那么<span class=”msg”>[^<]{1,480}</span>這種寫法的思路要好于<span class=”msg”>.*</span>旺嬉,原因有二:一是使用[^<]管行,它保證了文本的范圍不會超出下一個小于號所在的位置;二是明確長度范圍邪媳,{1,480}捐顷,其依據(jù)是一條twitter消息大致能的字符長度范圍荡陷。當(dāng)然,480這個長度是否正確還可推敲迅涮,但是這種思路是值得借鑒的废赞。說得狠一點,“濫用點號叮姑、星號和加號是不環(huán)保唉地、不負(fù)責(zé)任的做法”。
不要讓稻草壓死駱駝传透。每使用一個普通括號()而不是非捕獲型括號(?:…)耘沼,就會保留一部分內(nèi)存等著你再次訪問。這樣的正則表達式朱盐、無限次地運行次數(shù)群嗤,無異于一根根稻草的堆加,終于能將駱駝壓死兵琳。養(yǎng)成合理使用(?:…)括號的習(xí)慣狂秘。
寧簡勿繁。將一條復(fù)雜的正則表達式拆分為兩條或多條簡單的正則表達式躯肌,編程難度會降低赃绊,運行效率會提升。例如用來消除行首和行尾空白字符的正則表達式s/^\s+|\s+//g; 碧查。這個例子出自《精通正則表達式》第五章,書中對它的評論是“它幾乎總是最快的校仑,而且顯然最容易理解”忠售。既快又容易理解,何樂而不為迄沫?工作中我們還有其它的理由要將C==(A|B)這樣的正則表達式拆為A和B兩條表達式分別執(zhí)行稻扬。例如,雖然A和B這兩種情況只要有一種能夠擊中所需要的文本模式就會成功匹配羊瘩,但是如果只要有一條子表達式(例如A)會產(chǎn)生誤匹配泰佳,那么不論其它的子表達式(例如B)效率如何之高,范圍如何精準(zhǔn)尘吗,C的總體精準(zhǔn)度也會因A而受到影響逝她。
巧妙定位。有時候睬捶,我們需要匹配的the黔宛,是作為單詞的the(兩邊有空格),而不是作為單詞一部分的t-h-e的有序排列(例如together中的the)擒贸。在適當(dāng)?shù)臅r候用上^臀晃,$觉渴,\b等等定位錨點,能有效提升找到成功匹配徽惋、淘汰不成功匹配的效率案淋。