正則表達(dá)式是對字符串(包括普通字符(例如,a 到 z 之間的字母)和特殊字符(稱為“元字符”))操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個(gè)“規(guī)則字符串”,這個(gè)“規(guī)則字符串”用來表達(dá)對字符串的一種過濾邏輯。正則表達(dá)式是一種文本模式,該模式描述在搜索文本時(shí)要匹配的一個(gè)或多個(gè)字符串。
正則表達(dá)式是什么?
正則表達(dá)式,又稱規(guī)則表達(dá)式。(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),計(jì)算機(jī)科學(xué)的一個(gè)概念。正則表達(dá)式通常被用來檢索、替換那些符合某個(gè)模式(規(guī)則)的文本。
許多程序設(shè)計(jì)語言都支持利用正則表達(dá)式進(jìn)行字符串操作。例如,在Perl中就內(nèi)建了一個(gè)功能強(qiáng)大的正則表達(dá)式引擎。正則表達(dá)式這個(gè)概念最初是由Unix中的工具軟件(例如sed和grep)普及開的。正則表達(dá)式通常縮寫成“regex”,單數(shù)有regexp、regex,復(fù)數(shù)有regexps、regexes、regexen。
正則表達(dá)式起源:
正則表達(dá)式的“鼻祖”或許可一直追溯到科學(xué)家對人類神經(jīng)系統(tǒng)工作原理的早期研究。美國新澤西州的Warren McCulloch和出生在美國底特律的Walter Pitts這兩位神經(jīng)生理方面的科學(xué)家,研究出了一種用數(shù)學(xué)方式來描述神經(jīng)網(wǎng)絡(luò)的新方法,他們創(chuàng)造性地將神經(jīng)系統(tǒng)中的神經(jīng)元描述成了小而簡單的自動(dòng)控制元,從而作出了一項(xiàng)偉大的工作革新。
在1951 年,一位名叫Stephen Kleene的數(shù)學(xué)科學(xué)家,他在Warren McCulloch和Walter Pitts早期工作的基礎(chǔ)之上,發(fā)表了一篇題目是《神經(jīng)網(wǎng)事件的表示法》的論文,利用稱之為正則集合的數(shù)學(xué)符號(hào)來描述此模型,引入了正則表達(dá)式的概念。正則表達(dá)式被作為用來描述其稱之為“正則集的代數(shù)”的一種表達(dá)式,因而采用了“正則表達(dá)式”這個(gè)術(shù)語。
之后一段時(shí)間,人們發(fā)現(xiàn)可以將這一工作成果應(yīng)用于其他方面。Ken Thompson就把這一成果應(yīng)用于計(jì)算搜索算法的一些早期研究,Ken Thompson是 Unix的主要發(fā)明人,也就是大名鼎鼎的Unix之父。Unix之父將此符號(hào)系統(tǒng)引入編輯器QED,然后是Unix上的編輯器ed,并最終引入grep。Jeffrey Friedl 在其著作《Mastering Regular Expressions (2nd edition)》(中文版譯作:精通正則表達(dá)式,已出到第三版)中對此作了進(jìn)一步闡述講解,如果你希望更多了解正則表達(dá)式理論和歷史,推薦你看看這本書。
自此以后,正則表達(dá)式被廣泛地應(yīng)用到各種UNIX或類似于UNIX的工具中,如大家熟知的Perl。Perl的正則表達(dá)式源自于Henry Spencer編寫的regex,之后已演化成了pcre(Perl兼容正則表達(dá)式Perl Compatible Regular Expressions),pcre是一個(gè)由Philip Hazel開發(fā)的、為很多現(xiàn)代工具所使用的庫。正則表達(dá)式的第一個(gè)實(shí)用應(yīng)用程序即為Unix中的 qed 編輯器。
然后,正則表達(dá)式在各種計(jì)算機(jī)語言或各種應(yīng)用領(lǐng)域得到了廣大的應(yīng)用和發(fā)展,演變成為計(jì)算機(jī)技術(shù)森林中的一只形神美麗且聲音動(dòng)聽的百靈鳥。
以上是關(guān)于正則表達(dá)式的起源和發(fā)展的歷史描述,如今正則表達(dá)式在基于文本的編輯器和搜索工具中依然占據(jù)著一個(gè)非常重要的地位。
在最近的六十年中,正則表達(dá)式逐漸從模糊而深?yuàn)W的數(shù)學(xué)概念,發(fā)展成為在計(jì)算機(jī)各類工具和軟件包應(yīng)用中的主要功能。不僅僅眾多UNIX工具支持正則表達(dá)式,近二十年來,在WINDOWS的陣營下,正則表達(dá)式的思想和應(yīng)用在大部分 Windows 開發(fā)者工具包中得到支持和嵌入應(yīng)用!從正則表達(dá)式在Microsoft Visual Basic 6 或 Microsoft VBScript到.NET Framework中的探索和發(fā)展,WINDOWS系列產(chǎn)品對正則表達(dá)式的支持發(fā)展到無與倫比的高度,幾乎所有 Microsoft 開發(fā)者和所有.NET語言都可以使用正則表達(dá)式。如果你是一位接觸計(jì)算機(jī)語言的工作者,那么你會(huì)在主流操作系統(tǒng)(*nix[Linux, Unix等]、Windows、HP、BeOS等)、主流的開發(fā)語言(delphi、Scala、PHP、C#、Java、C++、Objective-c、Swift、VB、Javascript、Ruby以及Python等)、數(shù)以億萬計(jì)的各種應(yīng)用軟件中,都可以看到正則表達(dá)式優(yōu)美的舞姿。
正則表達(dá)式符號(hào)
(摘自《正則表達(dá)式之道》)
正則表達(dá)式由一些普通字符和一些元字符(metacharacters)組成。普通字符包括大小寫的字母和數(shù)字,而元字符則具有特殊的含義,我們下面會(huì)給予解釋。
在最簡單的情況下,一個(gè)正則表達(dá)式看上去就是一個(gè)普通的查找串。例如,正則表達(dá)式"testing"中沒有包含任何元字符,它可以匹配"testing"和"testing123"等字符串,但是不能匹配"Testing"。
要想真正的用好正則表達(dá)式,正確的理解元字符是最重要的事情。下表列出了所有的元字符和對它們的一個(gè)簡短的描述。
元字符 |
描述 |
\ |
將下一個(gè)字符標(biāo)記符、或一個(gè)向后引用、或一個(gè)八進(jìn)制轉(zhuǎn)義符。例如,“\\n”匹配\n。“\n”匹配換行符。序列“\\”匹配“\”而“\(”則匹配“(”。即相當(dāng)于多種編程語言中都有的“轉(zhuǎn)義字符”的概念。 |
^ |
匹配輸入字行首。如果設(shè)置了RegExp對象的Multiline屬性,^也匹配“\n”或“\r”之后的位置。 |
$ |
匹配輸入行尾。如果設(shè)置了RegExp對象的Multiline屬性,$也匹配“\n”或“\r”之前的位置。 |
* |
匹配前面的子表達(dá)式任意次。例如,zo*能匹配“z”,也能匹配“zo”以及“zoo”。*等價(jià)于{0,}。 |
+ |
匹配前面的子表達(dá)式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價(jià)于{1,}。 |
? |
匹配前面的子表達(dá)式零次或一次。例如,“do(es)?”可以匹配“do”或“does”。?等價(jià)于{0,1}。 |
{n} |
n是一個(gè)非負(fù)整數(shù)。匹配確定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的兩個(gè)o。 |
{n,} |
n是一個(gè)非負(fù)整數(shù)。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等價(jià)于“o+”。“o{0,}”則等價(jià)于“o*”。 |
{n,m} |
m和n均為非負(fù)整數(shù),其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”將匹配“fooooood”中的前三個(gè)o為一組,后三個(gè)o為一組。“o{0,1}”等價(jià)于“o?”。請注意在逗號(hào)和兩個(gè)數(shù)之間不能有空格。 |
? |
當(dāng)該字符緊跟在任何一個(gè)其他限制符(*,+,?,{n},{n,},{n,m})后面時(shí),匹配模式是非貪婪的。非貪婪模式盡可能少地匹配所搜索的字符串,而默認(rèn)的貪婪模式則盡可能多地匹配所搜索的字符串。例如,對于字符串“oooo”,“o+”將盡可能多地匹配“o”,得到結(jié)果[“oooo”],而“o+?”將盡可能少地匹配“o”,得到結(jié)果 ['o', 'o', 'o', 'o'] |
.點(diǎn) |
匹配除“\n”和"\r"之外的任何單個(gè)字符。要匹配包括“\n”和"\r"在內(nèi)的任何字符,請使用像“[\s\S]”的模式。 |
(pattern) |
匹配pattern并獲取這一匹配。所獲取的匹配可以從產(chǎn)生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中則使用$0…$9屬性。要匹配圓括號(hào)字符,請使用“\(”或“\)”。 |
(?:pattern) |
非獲取匹配,匹配pattern但不獲取匹配結(jié)果,不進(jìn)行存儲(chǔ)供以后使用。這在使用或字符“(|)”來組合一個(gè)模式的各個(gè)部分時(shí)很有用。例如“industr(?:y|ies)”就是一個(gè)比“industry|industries”更簡略的表達(dá)式。 |
(?=pattern) |
非獲取匹配,正向肯定預(yù)查,在任何匹配pattern的字符串開始處匹配查找字符串,該匹配不需要獲取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。預(yù)查不消耗字符,也就是說,在一個(gè)匹配發(fā)生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預(yù)查的字符之后開始。 |
(?!pattern) |
非獲取匹配,正向否定預(yù)查,在任何不匹配pattern的字符串開始處匹配查找字符串,該匹配不需要獲取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。 |
(?<=pattern) |
非獲取匹配,反向肯定預(yù)查,與正向肯定預(yù)查類似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。 *python的正則表達(dá)式?jīng)]有完全按照正則表達(dá)式規(guī)范實(shí)現(xiàn),所以一些高級(jí)特性建議使用其他語言如java、scala等 |
(?<!patte_n) |
非獲取匹配,反向否定預(yù)查,與正向否定預(yù)查類似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。 *python的正則表達(dá)式?jīng)]有完全按照正則表達(dá)式規(guī)范實(shí)現(xiàn),所以一些高級(jí)特性建議使用其他語言如java、scala等 |
x|y |
匹配x或y。例如,“z|food”能匹配“z”或“food”(此處請謹(jǐn)慎)。“[z|f]ood”則匹配“zood”或“food”。 |
[xyz] |
字符集合。匹配所包含的任意一個(gè)字符。例如,“[abc]”可以匹配“plain”中的“a”。 |
[^xyz] |
負(fù)值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”任一字符。 |
[a-z] |
字符范圍。匹配指定范圍內(nèi)的任意字符。例如,“[a-z]”可以匹配“a”到“z”范圍內(nèi)的任意小寫字母字符。 注意:只有連字符在字符組內(nèi)部時(shí),并且出現(xiàn)在兩個(gè)字符之間時(shí),才能表示字符的范圍; 如果出字符組的開頭,則只能表示連字符本身. |
[^a-z] |
負(fù)值字符范圍。匹配任何不在指定范圍內(nèi)的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范圍內(nèi)的任意字符。 |
\b |
匹配一個(gè)單詞的邊界,也就是指單詞和空格間的位置(即正則表達(dá)式的“匹配”有兩種概念,一種是匹配字符,一種是匹配位置,這里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“\b1_”可以匹配“1_23”中的“1_”,但不能匹配“21_3”中的“1_”。 |
\B |
匹配非單詞邊界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。 |
\cx |
匹配由x指明的控制字符。例如,\cM匹配一個(gè)Control-M或回車符。x的值必須為A-Z或a-z之一。否則,將c視為一個(gè)原義的“c”字符。 |
\d |
匹配一個(gè)數(shù)字字符。等價(jià)于[0-9]。grep 要加上-P,perl正則支持 |
\D |
匹配一個(gè)非數(shù)字字符。等價(jià)于[^0-9]。grep要加上-P,perl正則支持 |
\f |
匹配一個(gè)換頁符。等價(jià)于\x0c和\cL。 |
\n |
匹配一個(gè)換行符。等價(jià)于\x0a和\cJ。 |
\r |
匹配一個(gè)回車符。等價(jià)于\x0d和\cM。 |
\s |
匹配任何不可見字符,包括空格、制表符、換頁符等等。等價(jià)于[ \f\n\r\t\v]。 |
\S |
匹配任何可見字符。等價(jià)于[^ \f\n\r\t\v]。 |
\t |
匹配一個(gè)制表符。等價(jià)于\x09和\cI。 |
\v |
匹配一個(gè)垂直制表符。等價(jià)于\x0b和\cK。 |
\w |
匹配包括下劃線的任何單詞字符。類似但不等價(jià)于“[A-Za-z0-9_]”,這里的"單詞"字符使用Unicode字符集。 |
\W |
匹配任何非單詞字符。等價(jià)于“[^A-Za-z0-9_]”。 |
\xn |
匹配n,其中n為十六進(jìn)制轉(zhuǎn)義值。十六進(jìn)制轉(zhuǎn)義值必須為確定的兩個(gè)數(shù)字長。例如,“\x41”匹配“A”。“\x041”則等價(jià)于“\x04&1”。正則表達(dá)式中可以使用ASCII編碼。 |
\num |
匹配num,其中num是一個(gè)正整數(shù)。對所獲取的匹配的引用。例如,“(.)\1”匹配兩個(gè)連續(xù)的相同字符。 |
\n |
標(biāo)識(shí)一個(gè)八進(jìn)制轉(zhuǎn)義值或一個(gè)向后引用。如果\n之前至少n個(gè)獲取的子表達(dá)式,則n為向后引用。否則,如果n為八進(jìn)制數(shù)字(0-7),則n為一個(gè)八進(jìn)制轉(zhuǎn)義值。 |
\nm |
標(biāo)識(shí)一個(gè)八進(jìn)制轉(zhuǎn)義值或一個(gè)向后引用。如果\nm之前至少有nm個(gè)獲得子表達(dá)式,則nm為向后引用。如果\nm之前至少有n個(gè)獲取,則n為一個(gè)后跟文字m的向后引用。如果前面的條件都不滿足,若n和m均為八進(jìn)制數(shù)字(0-7),則\nm將匹配八進(jìn)制轉(zhuǎn)義值nm。 |
\nml |
如果n為八進(jìn)制數(shù)字(0-7),且m和l均為八進(jìn)制數(shù)字(0-7),則匹配八進(jìn)制轉(zhuǎn)義值nml。 |
\un |
匹配n,其中n是一個(gè)用四個(gè)十六進(jìn)制數(shù)字表示的Unicode字符。例如,\u00A9匹配版權(quán)符號(hào)(©)。 |
\p{P} |
小寫 p 是 property 的意思,表示 Unicode 屬性,用于 Unicode 正表達(dá)式的前綴。中括號(hào)內(nèi)的“P”表示Unicode 字符集七個(gè)字符屬性之一:標(biāo)點(diǎn)字符。 其他六個(gè)屬性: L:字母; M:標(biāo)記符號(hào)(一般不會(huì)單獨(dú)出現(xiàn)); Z:分隔符(比如空格、換行等); S:符號(hào)(比如數(shù)學(xué)符號(hào)、貨幣符號(hào)等); N:數(shù)字(比如阿拉伯?dāng)?shù)字、羅馬數(shù)字等); C:其他字符。 *注:此語法部分語言不支持,例:javascript。 |
\< \> |
匹配詞(word)的開始(\<)和結(jié)束(\>)。例如正則表達(dá)式\<the\>能夠匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:這個(gè)元字符不是所有的軟件都支持的。 |
( ) | 將( 和 ) 之間的表達(dá)式定義為“組”(group),并且將匹配這個(gè)表達(dá)式的字符保存到一個(gè)臨時(shí)區(qū)域(一個(gè)正則表達(dá)式中最多可以保存9個(gè)),它們可以用 \1 到\9 的符號(hào)來引用。 |
| | 將兩個(gè)匹配條件進(jìn)行邏輯“或”(or)運(yùn)算。例如正則表達(dá)式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:這個(gè)元字符不是所有的軟件都支持的。 |