常用的正規表達式
常用正規表達式
正規表達式用于字元串處理、表單驗證等場合,實用高效。現将一些常用的表達式收集于此,以備不時之需。
使用者名:/^[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 标簽:/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/
Unicode編碼中的漢字範圍:/^[u4e00-u9fa5],{0,}$/
比對中文字元的正規表達式: [\u4e00-\u9fa5]
評注:比對中文還真是個頭疼的事,有了這個表達式就好辦了
比對雙位元組字元(包括漢字在内):[^\x00-\xff]
評注:可以用來計算字元串的長度(一個雙位元組字元長度計2,ASCII字元計1)
比對空白行的正規表達式:\n\s*\r
評注:可以用來删除空白行
比對HTML标記的正規表達式:<(\S*?)[^>]*>.*?</\1>|<.*? />
評注:網上流傳的版本太糟糕,上面這個也僅僅能比對部分,對于複雜的嵌套标記依舊無能為力
比對首尾空白字元的正規表達式:^\s*|\s*$
評注:可以用來删除行首行尾的空白字元(包括空格、制表符、換頁符等等),非常有用的表達式
比對Email位址的正規表達式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
評注:表單驗證時很實用
比對網址URL的正規表達式:[a-zA-z]+://[^\s]*
評注:網上流傳的版本功能很有限,上面這個基本可以滿足需求
比對帳号是否合法(字母開頭,允許5-16位元組,允許字母數字下劃線):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
比對國内電話号碼:\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位數字
比對身份證:\d{15}|\d{18}
評注:中國大陸的身份證為15位或18位
比對ip位址:\d+\.\d+\.\d+\.\d+
評注:提取ip位址時有用
比對特定數字:
^[1-9]\d*$ //比對正整數
^-[1-9]\d*$ //比對負整數
^-?[1-9]\d*$ //比對整數
^[1-9]\d*|0$ //比對非負整數(正整數 + 0)
^-[1-9]\d*|0$ //比對非正整數(負整數 + 0)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ //比對正浮點數
^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ //比對負浮點數
^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$ //比對浮點數
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$ //比對非負浮點數(正浮點數 + 0)
^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$ //比對非正浮點數(負浮點數 + 0)
評注:處理大量資料時有用,具體應用時注意修正
比對特定字元串:
^[A-Za-z]+$ //比對由26個英文字母組成的字元串
^[A-Z]+$ //比對由26個英文字母的大寫組成的字元串
^[a-z]+$ //比對由26個英文字母的小寫組成的字元串
^[A-Za-z0-9]+$ //比對由數字和26個英文字母組成的字元串
^\w+$ //比對由數字、26個英文字母或者下劃線組成的字元串
表達式全集
正規表達式有多種不同的風格。下表是在PCRE中元字元及其在正規表達式上下文中的行為的一個完整清單:
字元 | 描述 |
---|---|
\ | 将下一個字元标記為一個特殊字元、或一個原義字元、或一個向後引用、或一個八進制轉義符。例如,“ ”比對字元“ ”。“ ”比對一個換行符。序列“ ”比對“ ”而“ ”則比對“ ”。 |
^ | 比對輸入字元串的開始位置。如果設定了RegExp對象的Multiline屬性,^也比對“ ”或“ ”之後的位置。 |
$ | 比對輸入字元串的結束位置。如果設定了RegExp對象的Multiline屬性,$也比對“ ”之前的位置。 |
* | 比對前面的子表達式零次或多次。例如,zo*能比對“ ”以及“ ”。*等價于{0,}。 |
+ | 比對前面的子表達式一次或多次。例如,“ ”能比對“ ”,但不能比對“ ”。+等價于{1,}。 |
? | 比對前面的子表達式零次或一次。例如,“ ”可以比對“ ”中的“ ”。?等價于{0,1}。 |
{n} | n是一個非負整數。比對确定的n次。例如,“ ”不能比對“ ”,但是能比對“ ”中的兩個o。 |
{n,} | n是一個非負整數。至少比對n次。例如,“ ”,但能比對“ ”中的所有o。“ ”等價于“ ”則等價于“ |
{n,m} | m和n均為非負整數,其中n<=m。最少比對n次且最多比對m次。例如,“ ”将比對“ ”中的前三個o。“ ”。請注意在逗号和兩個數之間不能有空格。 |
當該字元緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})後面時,比對模式是非貪婪的。非貪婪模式盡可能少的比對所搜尋的字元串,而預設的貪婪模式則盡可能多的比對所搜尋的字元串。例如,對于字元串“ ”,“ ”将比對單個“ ”,而“ ”将比對所有“ | |
. | 比對除“ ”之外的任何單個字元。要比對包括“ ”在内的任何字元,請使用像“ ”的模式。 |
(pattern) | 比對pattern并擷取這一比對。所擷取的比對可以從産生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中則使用$0…$9屬性。要比對圓括号字元,請使用“ |
(?:pattern) | 比對pattern但不擷取比對結果,也就是說這是一個非擷取比對,不進行存儲供以後使用。這在使用或字元“ ”來組合一個模式的各個部分是很有用。例如“ ”就是一個比“ ”更簡略的表達式。 |
(?=pattern) | 正向預查,在任何比對pattern的字元串開始處比對查找字元串。這是一個非擷取比對,也就是說,該比對不需要擷取供以後使用。例如,“ ”。預查不消耗字元,也就是說,在一個比對發生後,在最後一次比對之後立即開始下一次比對的搜尋,而不是從包含預查的字元之後開始。 |
(?!pattern) | 負向預查,在任何不比對pattern的字元串開始處比對查找字元串。這是一個非擷取比對,也就是說,該比對不需要擷取供以後使用。例如“ ”。預查不消耗字元,也就是說,在一個比對發生後,在最後一次比對之後立即開始下一次比對的搜尋,而不是從包含預查的字元之後開始 |
x|y | 比對x或y。例如,“ |
[xyz] | 字元集合。比對所包含的任意一個字元。例如,“ |
[^xyz] | 負值字元集合。比對未包含的任意字元。例如,“ |
[a-z] | 字元範圍。比對指定範圍内的任意字元。例如,“ ”到“ ”範圍内的任意小寫字母字元。 |
[^a-z] | 負值字元範圍。比對任何不在指定範圍内的任意字元。例如,“ ”可以比對任何不在“ ”範圍内的任意字元。 |
\b | 比對一個單詞邊界,也就是指單詞和空格間的位置。例如,“ |
\B | 比對非單詞邊界。“ |
\cx | 比對由x指明的控制字元。例如,\cM比對一個Control-M或回車符。x的值必須為A-Z或a-z之一。否則,将c視為一個原義的“ ”字元。 |
\d | 比對一個數字字元。等價于[0-9]。 |
\D | 比對一個非數字字元。等價于[^0-9]。 |
\f | 比對一個換頁符。等價于\x0c和\cL。 |
\n | 比對一個換行符。等價于\x0a和\cJ。 |
\r | 比對一個回車符。等價于\x0d和\cM。 |
\s | 比對任何空白字元,包括空格、制表符、換頁符等等。等價于[\f\n\r\t\v]。 |
\S | 比對任何非空白字元。等價于[^\f\n\r\t\v]。 |
\t | 比對一個制表符。等價于\x09和\cI。 |
\v | 比對一個垂直制表符。等價于\x0b和\cK。 |
\w | 比對包括下劃線的任何單詞字元。等價于“ |
\W | 比對任何非單詞字元。等價于“ |
\xn | 比對n,其中n為十六進制轉義值。十六進制轉義值必須為确定的兩個數字長。例如,“ ”。正規表達式中可以使用ASCII編碼。. |
\num | 比對num,其中num是一個正整數。對所擷取的比對的引用。例如,“ ”比對兩個連續的相同字元。 |
辨別一個八進制轉義值或一個向後引用。如果\n之前至少n個擷取的子表達式,則n為向後引用。否則,如果n為八進制數字(0-7),則n為一個八進制轉義值。 | |
\nm | 辨別一個八進制轉義值或一個向後引用。如果\nm之前至少有nm個獲得子表達式,則nm為向後引用。如果\nm之前至少有n個擷取,則n為一個後跟文字m的向後引用。如果前面的條件都不滿足,若n和m均為八進制數字(0-7),則\nm将比對八進制轉義值nm。 |
\nml | 如果n為八進制數字(0-3),且m和l均為八進制數字(0-7),則比對八進制轉義值nml。 |
\un | 比對n,其中n是一個用四個十六進制數字表示的Unicode字元。例如,\u00A9比對版權符号(?)。 |
以下是以PHP的文法所寫的示例
驗證字元串是否隻含數字與英文,字元串長度并在4~16個字元之間
<?php
$str = 'a1234';
if (preg_match("^[a-zA-Z0-9]{4,16}$", $str)) {
echo "驗證成功";
} else {
echo "驗證失敗";
}
?>
簡易的台灣身份證字号驗證
<?php
$str = 'a1234';
if (preg_match("/^\w[12]\d{8}$/", $str)) {
echo "驗證成功";
} else {
echo "驗證失敗";
}
?>
以下示例是用 Perl 語言寫的,與上面的示例功能相同
print $str = "a1234" =~ m:^[a-zA-Z0-9]{4,16}$: ? "COMFIRM" : "FAILED";
print $str = "a1234" =~ m"^\w[12]\d{8}$" ? "COMFIRM" : "INVAILD";
如何寫出高效率的正規表達式
如果純粹是為了挑戰自己的正則水準,用來實作一些特效(例如使用正規表達式計算質數、解線性方程),效率不是問題;如果所寫的正規表達式隻是為了滿足一兩次、幾十次的運作,優化與否差別也不太大。但是,如果所寫的正規表達式會百萬次、千萬次地運作,效率就是很大的問題了。我這裡總結了幾條提升正規表達式運作效率的經驗(工作中學到的,看書學來的,自己的體會),貼在這裡。如果您有其它的經驗而這裡沒有提及,歡迎賜教。
為行文友善,先定義兩個概念。
誤比對:指正規表達式所比對的内容範圍超出了所需要範圍,有些文本明明不符合要求,但是被所寫的正則式“擊中了”。例如,如果使用\d{11}來比對11位的手機号,\d{11}不單能比對正确的手機号,它還會比對98765432100這樣的明顯不是手機号的字元串。我們把這樣的比對稱之為誤比對。
漏比對:指正規表達式所比對的内容所規定的範圍太狹窄,有些文本确實是所需要的,但是所寫的正則沒有将這種情況囊括在内。例如,使用\d{18}來比對18位的身份證号碼,就會漏掉結尾是字母X的情況。
寫出一條正規表達式,既可能隻出現誤比對(條件寫得極寬松,其範圍大于目标文本),也可能隻出現漏比對(隻描述了目标文本中多種情況種的一種),還可能既有誤比對又有漏比對。例如,使用\w+\.com來比對.com結尾的域名,既會誤比對abc_.com這樣的字串(合法的域名中不含下劃線,\w包含了下劃線這種情況),又會漏掉ab-c.com這樣的域名(合法域名中可以含中劃線,但是\w不比對中劃線)。
精準的正規表達式意味着既無誤比對且無漏比對。當然,現實中存在這樣的情況:隻能看到有限數量的文本,根據這些文本寫規則,但是這些規則将會用到海量的文本中。這種情況下,盡可能地(如果不是完全地)消除誤比對以及漏比對,并提升運作效率,就是我們的目标。本文所提出的經驗,主要是針對這種情況。
掌握文法細節。正規表達式在各種語言中,其文法大緻相同,細節各有千秋。明确所使用語言的正則的文法的細節,是寫出正确、高效正規表達式的基礎。例如,perl中與\w等效的比對範圍是[a-zA-Z0-9_];perl正則式不支援肯定逆序環視中使用可變的重複(variable repetition inside lookbehind,例如(?<=.*)abc),但是.Net文法是支援這一特性的;又如,JavaScript連逆序環視(Lookbehind,如(?<=ab)c)都不支援,而perl和python是支援的。《精通正規表達式》第3章《正規表達式的特性和流派概覽》明确地列出了各大派系正則的異同,這篇文章也簡要地列出了幾種常用語言、工具中正則的比較。對于具體使用者而言,至少應該詳細了解正在使用的那種工作語言裡正則的文法細節。
先粗後精,先加後減。使用正規表達式文法對于目标文本進行描述和界定,可以像畫素描一樣,先大緻勾勒出架構,再逐漸在局步實作細節。仍舉剛才的手機号的例子,先界定\d{11},總不會錯;再細化為1[358]\d{9},就向前邁了一大步(至于第二位是不是3、5、8,這裡無意深究,隻舉這樣一個例子,說明逐漸細化的過程)。這樣做的目的是先消除漏比對(剛開始先盡可能多地比對,做加法),然後再一點一點地消除誤比對(做減法)。這樣有先有後,在考慮時才不易出錯,進而向“不誤不漏”這個目标邁進。
留有餘地。所能看到的文本sample是有限的,而待比對檢驗的文本是海量的,暫時不可見的。對于這樣的情況,在寫正規表達式時要跳出所能見到的文本的圈子,開拓思路,作出“戰略性前瞻”。例如,經常收到這樣的垃圾短信:“發*票”、“發#漂”。如果要寫規則屏蔽這樣煩人的垃圾短信,不但要能寫出可以比對目前文本的正規表達式 發[*#](?:票|漂),還要能夠想到 發.(?:票|漂|飄)之類可能出現的“變種”。這在具體的領域或許會有針對性的規則,不多言。這樣做的目的是消除漏比對,延長正規表達式的生命周期。
明确。具體說來,就是謹慎用點号這樣的元字元,盡可能不用星号和加号這樣的任意量詞。隻要能确定範圍的,例如\w,就不要用點号;隻要能夠預測重複次數的,就不要用任意量詞。例如,寫析取twitter消息的腳本,假設一條消息的xml正文部分結構是<span class=”msg”>…</span>且正文中無尖括号,那麼<span class=”msg”>[^<]{1,480}</span>這種寫法的思路要好于<span class=”msg”>.*</span>,原因有二:一是使用[^<],它保證了文本的範圍不會超出下一個小于号所在的位置;二是明确長度範圍,{1,480},其依據是一條twitter消息大緻能的字元長度範圍。當然,480這個長度是否正确還可推敲,但是這種思路是值得借鑒的。說得狠一點,“濫用點号、星号和加号是不環保、不負責任的做法”。
不要讓稻草壓死駱駝。每使用一個普通括号()而不是非捕獲型括号(?:…),就會保留一部分記憶體等着你再次通路。這樣的正規表達式、無限次地運作次數,無異于一根根稻草的堆加,終于能将駱駝壓死。養成合理使用(?:…)括号的習慣。
甯簡勿繁。将一條複雜的正規表達式拆分為兩條或多條簡單的正規表達式,程式設計難度會降低,運作效率會提升。例如用來消除行首和行尾空白字元的正規表達式s/^\s+|\s+$//g;,其運作效率理論上要低于s/^\s+//g; s/\s+$//g; 。這個例子出自《精通正規表達式》第五章,書中對它的評論是“它幾乎總是最快的,而且顯然最容易了解”。既快又容易了解,何樂而不為?工作中我們還有其它的理由要将C==(A|B)這樣的正規表達式拆為A和B兩條表達式分别執行。例如,雖然A和B這兩種情況隻要有一種能夠擊中所需要的文本模式就會成功比對,但是如果隻要有一條子表達式(例如A)會産生誤比對,那麼不論其它的子表達式(例如B)效率如何之高,範圍如何精準,C的總體精準度也會因A而受到影響。
巧妙定位。有時候,我們需要比對的the,是作為單詞的the(兩邊有空格),而不是作為單詞一部分的t-h-e的有序排列(例如together中的the)。在适當的時候用上^,$,\b等等定位錨點,能有效提升找到成功比對、淘汰不成功比對的效率。
正規表達式: http://114.xixik.com/regex/
HTML轉義字元: http://114.xixik.com/character/