天天看點

常用的正規表達式

常用的正規表達式

常用正規表達式

正規表達式用于字元串處理、表單驗證等場合,實用高效。現将一些常用的表達式收集于此,以備不時之需。

使用者名:/^[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中元字元及其在正規表達式上下文中的行為的一個完整清單:

字元 描述
\ 将下一個字元标記為一個特殊字元、或一個原義字元、或一個向後引用、或一個八進制轉義符。例如,“

n

”比對字元“

n

”。“

\n

”比對一個換行符。序列“

\\

”比對“

\

”而“

\(

”則比對“

(

”。
^ 比對輸入字元串的開始位置。如果設定了RegExp對象的Multiline屬性,^也比對“

\n

”或“

\r

”之後的位置。
$ 比對輸入字元串的結束位置。如果設定了RegExp對象的Multiline屬性,$也比對“

\n

\r

”之前的位置。
* 比對前面的子表達式零次或多次。例如,zo*能比對“

z

”以及“

zoo

”。*等價于{0,}。
+ 比對前面的子表達式一次或多次。例如,“

zo+

”能比對“

zo

zoo

”,但不能比對“

z

”。+等價于{1,}。
? 比對前面的子表達式零次或一次。例如,“

do(es)?

”可以比對“

do

does

”中的“

do

”。?等價于{0,1}。
{n} n是一個非負整數。比對确定的n次。例如,“

o{2}

”不能比對“

Bob

o

”,但是能比對“

food

”中的兩個o。
{n,} n是一個非負整數。至少比對n次。例如,“

o{2,}

Bob

o

”,但能比對“

foooood

”中的所有o。“

o{1,}

”等價于“

o+

o{0,}

”則等價于“

o*

{n,m} m和n均為非負整數,其中n<=m。最少比對n次且最多比對m次。例如,“

o{1,3}

”将比對“

fooooood

”中的前三個o。“

o{0,1}

o?

”。請注意在逗号和兩個數之間不能有空格。
當該字元緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})後面時,比對模式是非貪婪的。非貪婪模式盡可能少的比對所搜尋的字元串,而預設的貪婪模式則盡可能多的比對所搜尋的字元串。例如,對于字元串“

oooo

”,“

o+?

”将比對單個“

o

”,而“

o+

”将比對所有“

o

. 比對除“

\

n

”之外的任何單個字元。要比對包括“

\

n

”在内的任何字元,請使用像“

[.\

n

]

”的模式。
(pattern) 比對pattern并擷取這一比對。所擷取的比對可以從産生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中則使用$0…$9屬性。要比對圓括号字元,請使用“

\(

\)

(?:pattern) 比對pattern但不擷取比對結果,也就是說這是一個非擷取比對,不進行存儲供以後使用。這在使用或字元“

(|)

”來組合一個模式的各個部分是很有用。例如“

industr(?:y|ies)

”就是一個比“

industry|industries

”更簡略的表達式。
(?=pattern) 正向預查,在任何比對pattern的字元串開始處比對查找字元串。這是一個非擷取比對,也就是說,該比對不需要擷取供以後使用。例如,“

Windows(?=95|98|NT|2000)

Windows2000

Windows

Windows3.1

Windows

”。預查不消耗字元,也就是說,在一個比對發生後,在最後一次比對之後立即開始下一次比對的搜尋,而不是從包含預查的字元之後開始。
(?!pattern) 負向預查,在任何不比對pattern的字元串開始處比對查找字元串。這是一個非擷取比對,也就是說,該比對不需要擷取供以後使用。例如“

Windows(?!95|98|NT|2000)

Windows3.1

Windows

Windows2000

Windows

”。預查不消耗字元,也就是說,在一個比對發生後,在最後一次比對之後立即開始下一次比對的搜尋,而不是從包含預查的字元之後開始
x|y 比對x或y。例如,“

z|food

z

food

(z|f)ood

zood

food

[xyz] 字元集合。比對所包含的任意一個字元。例如,“

[abc]

plain

a

[^xyz] 負值字元集合。比對未包含的任意字元。例如,“

[^abc]

plain

p

[a-z] 字元範圍。比對指定範圍内的任意字元。例如,“

[a-z]

a

”到“

z

”範圍内的任意小寫字母字元。
[^a-z] 負值字元範圍。比對任何不在指定範圍内的任意字元。例如,“

[^a-z]

”可以比對任何不在“

a

z

”範圍内的任意字元。
\b 比對一個單詞邊界,也就是指單詞和空格間的位置。例如,“

er\b

never

er

verb

er

\B 比對非單詞邊界。“

er\B

verb

er

never

er

\cx 比對由x指明的控制字元。例如,\cM比對一個Control-M或回車符。x的值必須為A-Z或a-z之一。否則,将c視為一個原義的“

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 比對包括下劃線的任何單詞字元。等價于“

[A-Za-z0-9_]

\W 比對任何非單詞字元。等價于“

[^A-Za-z0-9_]

\xn 比對n,其中n為十六進制轉義值。十六進制轉義值必須為确定的兩個數字長。例如,“

\x41

A

\x041

\x04&1

”。正規表達式中可以使用ASCII編碼。.
\num 比對num,其中num是一個正整數。對所擷取的比對的引用。例如,“

(.)\1

”比對兩個連續的相同字元。
辨別一個八進制轉義值或一個向後引用。如果\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/