PHP和Regex教程集合之一第1/2頁
來源:互聯網
上載者:User
PHP和Regex
一個Regex是一個特定的格式化模式,可以用來找出一個字串在另一個字串中的使用方式。幾個程式設計語言,包括Visual Basic,Perl,JavaScript和PHP都支援Regex,希望在這篇入門指導的結束,Mitchell(作者自己)可以讓你在PHP程式中能應用一些基本的Regex。Regex是在各種各樣的程式語言中突出的古怪特徵中的一種,但是由於它們看起來是很難的一個概念,所以很多開發人員就把它們放到了角落裡,忘記了它們的存在。
讓我們先來看看什麼是Regex,為什麼你要在PHP程式中用到它們。
什麼是Regex?
你對從一個不錯的老的基於控制的文字編輯器中分離出像BBEdit和notepad的程式,有什麼看法呢?兩個都支援文本輸入,可以讓你儲存文本到檔案中,但是現在的文字編輯器也支援其它功能,包括尋找–代替工具,這讓編輯一個文字檔相當容易。
Regex也是相似的,只是更好一些。Regex可以被認為一個極其進階的尋找-替換工具,讓我們從痛苦中擺脫出來:不必再寫定製的資料確認例子來檢查電子郵件地址或者來確認電話號碼的格式是正確的,如此等等。
任何程式中最普通的函數之一就是資料有效性檢查,PHP捆綁了一些文本檢查函數,允許我們用Regex匹配一個字串,確認有一個空格,有一個問號,等等。
你不知道的可能是,Regex可以簡單裝備嗎,當你掌握了一些Regex時(這個Regex可以用來告訴Regex引擎一個字串中我們想要匹配的部分),你會自問為什麼會把Regex扔到角落裡這麼久,^_^。
PHP有兩套函數,用來處理兩種類型的Regex:Perl5相容模式,和Posix標準相容模式。在這篇文章中我們將看看ereg函數,用遵照Posix標準的搜尋運算式工作。雖然它們並沒有Perl5模式那樣強大,但是一種不錯的學習Regex的方法。如果你對PHP支援的Perl5相容Regex感興趣,可以到PHP.net網站找一些關於preg函數的細節。
PHP有六個函數來處理Regex,它們都把一個Regex作為它們的第一個參數,列出如下:
ereg: 最常用的Regex函數, ereg 允許我們搜尋跟一個Regex匹配的一個字串.
ereg_replace: 允許我們搜尋跟Regex匹配的一個字串,並用新的字串代替所有這個運算式出現的地方。
eregi: 和ereg幾乎是一樣效果,不過忽略大小寫。
eregi_replace: 和ereg_replace有著一樣的搜尋-替換功能,不過忽略大小寫.
split: 允許我們搜尋和Regex匹配的字串,並且以字串集合的方式返回匹配結果.
spliti: split函數忽略大小寫版本.
為什麼使用Regex?
如果你不斷地建立不同的函數來檢查或者操作字串的一部分,現在你可能要放棄所有的這些函數,取而代之的用Regex。如果你對下列的問題都答“是的”,那麼你肯定要考慮使用Regex了:
你是否正在寫一些定製的函數來檢查表單資料(比如在電子信箱地址中的一個@,一個點)?
你是否寫一些定製的函數,在一個字串中迴圈每個字元,如果這個字元匹配了一個特定特徵(比如它是大寫的,或者它是一個空格),那麼就替換它?
除了是令人不舒服的字串檢查和操作方法,如果沒有有效率地寫代碼,上述的兩條也會使你的程式慢下來。你是否更傾向於用下面的代碼檢查一個電子信箱地址呢:
<?php
function validateEmail($email)
{
$hasAtSymbol = strpos($email, "@");
$hasDot = strpos($email, ".");
if($hasAtSymbol && $hasDot)
return true;
else
return false;
}
echo validateEmail("mitchell@devarticles.com");
?>
...
或者使用下面的代碼:
<?php
function validateEmail($email)
{
return ereg("^[a-zA-Z]+@[a-zA-Z]+.[a-zA-Z]+$", $email);
}
echo validateEmail("mitchell@devarticles.com");
?>
可以肯定的是,第一個函數比較容易,而且看起來結構也不錯。但是如果我們用上面的下一個版本的email地址檢查函數不是更容易嗎?
上面展示的第二個函數只用了Regex,包括了對ereg函數的一個調用。Ereg 函數返回true或者false,來聲明它的字串參數是否和Regex相匹配。
很多編程者避開Regex,只因為它們(在一些情況下)比其它的文本處理方法更慢。Regex可能慢的原因是因為它們涉及把字串在記憶體中拷貝和粘貼,因為Regex的每一個新的部分都對應匹配一個字串。但是,從我對Regex的經驗來說,除非你在文本中幾百個行運行一個複雜的Regex,否則效能上的缺陷都可以忽略不計,當把Regex作為輸入資料檢查工具時,也很少出現這種情況。
Regex文法
在你可以匹配一個字串到Regex之前,你必須先建立Regex。開始的時候,Regex的文法有點古怪,運算式中的每一個短語代表某個類型的搜尋特徵。下列是一些最普通的Regex,也都對應著一個如何使用它的例子:
字串頭部
搜尋一個字串的頭部,用^,例如
<?php echo ereg("^hello", "hello world!"); ?>
將返回 true, 但是
<?php echo ereg("^hello", "i say hello world"); ?>
將返回 false, 因為hello不在字串”I say hello world”的頭部。
字串尾部
搜尋字串尾部,用$,例如:
<?php echo ereg("bye$", "goodbye"); ?>
將返回true, 但是
<?php echo ereg("bye$", "goodbye my friend"); ?>
將返回 false,因為bye不在字串”goodbye my friend”的尾部.
任意的單個字元
搜尋任一字元,用點(.),例如:
<?php echo ereg(".", "cat"); ?>
將返回true,但是
<?php echo ereg(".", ""); ?>
將返回false,因為我們的要搜尋字串沒有包含字元。你可以用花括弧隨意告訴Regex引擎它要匹配多少個單個字元。如果我只想匹配5個字元,我可以這樣用ereg:
<?php echo ereg(".{5}$", "12345"); ?>
上面的這段代碼告訴Regex引擎若且唯若至少5個連續的字元出現字串的尾部時返回true.我們也可以限制連續出現的字元的數目:
<?php echo ereg("a{1,3}$", "aaa"); ?>
在上面的例子裡,我們已經告訴Regex引擎,我們的搜尋字串來匹配運算式,它在尾部必須有介於1和3個的”a”字元。
<?php echo ereg("a{1,3}$", "aaab"); ?>
上面的例子將不會返回true,雖然有三個”a”字元在搜尋字串裡,但是它們不是在字串的尾部。如果我們把結尾字串匹配$從Regex中去掉,那麼這個字串是匹配的。
我們也可以告訴Regex引擎來匹配至少有確定數目的字元在一行,如果它們存在的,可以匹配更多。 我們可以這樣做:
<?php echo ereg("a{3,}$", "aaaa"); ?>
零或多次重複字元
為了告訴Regex引擎一個字元可能存在,也可以重複,我們用*字元。這裡的兩個例子都將返回true.
<?php echo ereg("t*", "tom"); ?>
<?php echo ereg("t*", "fom"); ?>
即使第二個例子不包含”t”這個字元,但仍舊返回ture,因為*表示字元可以出現,但不是必須出現。事實上,任何普通的字串模式都會使上面的ereg調用返回true,因為't'字元是可選的.
一或多次重複字元
為了告訴Regex引擎一個字元必須存在,也可以重複不止一次,我們用+字元,像
<?php echo ereg("z+", "i like the zoo"); ?>
下面的例子也會返回true:
<?php echo ereg("z+", "i like the zzzzzzoo!"); ?>
零或一次重複字元
我們也可以告訴Regex引擎,一個字元必須是或者只存在一次,或者沒有。我們用?字元來做這項工作,就像
<?php echo ereg("c?", "cats are fuzzy"); ?>
如果我們願意,我們完全可以從上面的搜尋字串中刪除'c',這個運算式會仍舊返回true.'?' 的意思是一個'c'可以出現在搜尋字串的任何地方,但不是必須的。
Regex文法 (續)
空白字元
為了匹配一個搜尋字串中的空白字元,我們用預定義Posix的類,[[:space]].方括弧標明連續字元的相關性,”:space:”是實際要匹配的類(在這種情形下,是任何空白字元)。空白包括tab字元,新行字元,空白字元。或者,如果搜尋字串必須包含只有一個空格,而不是一個tab或者新行字元,你可以用一個空白字元(" ")。在大多數情況下,我傾向於使用":space:",因為這意味著我的意圖不僅僅是單個空白字元,這點很容易被忽視。這裡有一些Posix-標準預定義類,
有一些我們可以作為Regex的部分的一些Posix-標準預定義類,包括[:alnum:], [:digit:], [:lower:]等等。 完整的列表可以在這裡查看
我們可以像這樣匹配單個空白字元:
<?php echo ereg("Mitchell[[:space:]]Harper", "Mitchell Harper"); ?>
我們也可以通過在運算式後用?字元來告訴Regex引擎匹配沒有空白或者一個空白。
<?php echo ereg("Mitchell[[:space:]]?Harper", "MitchellHarper"); ?>
模式分組
相關的模式可以在方括弧裡分在一起。很容易用[a-z]和[A-Z]指定只有一個小寫字母或者一列大寫字母以搜尋字串的一部分存在。
<?php
// 要求從第一個到最後一個都是小寫字母
echo ereg("^[a-z]+$", "johndoe"); // 返回true
?>
或者像
<?php
// 要求從第一個到最後一個都是大寫字母
ereg("^[A-Z]+$", "JOHNDOE"); // 返回 true?
?>
我們也可以告訴Regex引擎,我們希望或者是小寫字母,或者是大寫字母。我們只要把[a-z]和[A-Z]模式結合在一起就可以做到。
<?php echo ereg("^[a-zA-Z]+$", "JohnDoe"); ?>
在上面的例子裡,如果我們能匹配"John Doe",而不是"JohnDoe",將是非常有意義的。我們用下面的Regex來做這個:
^[a-zA-Z]+[[:space:]]{1}[a-zA-Z]+$
很容易搜尋一個數字字串
<?php echo ereg("^[0-9]+$", "12345"); ?>
詞語分組
不僅僅搜尋模式可以分組,我們也可以用圓括弧把相關的搜尋字詞語進行分組。
<?php echo ereg("^(John|Jane).+$", "John Doe"); ?>
在上面的例子中,我們有一個字串頭部字元,緊跟著"John"或者"Jane",至少有一個其它字元,然後一個字串尾部字元。所以…
<?php echo ereg("^(John|Jane).+$", "Jane Doe"); ?>
...將也匹配我們的搜尋模式
特殊字元的情形
因為一些字元要用在一個搜尋模式的明確分組或者文法上,像在(John|Jane)中的圓括弧,我們需要告訴Regex引擎來屏蔽這些字元,加工它們使之成為被搜尋字串的一部分,而不是搜尋運算式的一部分。我們所用的方法稱為“字元轉義”,涉及到將任何“專用符號”加上反斜線。所以,例如,如果我想在我的搜尋中包含'|',那麼我就可以這樣做
<?php echo ereg("^[a-zA-z]+|[a-zA-z]+$", "John|Jane"); ?>
這裡只是少量的一些你要轉義的字元,你必須轉義^, $, (, ), ., [, |, *, ?, +, and { 。
希望你現在對Regex實際上有多麼強大有了一點點感覺了。現在讓我們看兩個用Regex來檢查資料中一個字串的例子。
Regex例子
例子1
讓我們把第一個例子做的相當簡單,檢驗一個標準的URL.一個標準的URL(沒有連接埠號碼),有三個部分構成:
[協議]://[網域名稱]
讓我們從匹配URL的協議部分開始,並且讓它只能用http或者ftp.我們可以用下面的Regex做到這點:
^(http|ftp)
^字元特指字串的頭部,利用圓括弧把http和ftp圍住,且用“或者”符號(|)將它們分開,我們告訴Regex引擎http和ftp兩者之一必須在字串的開頭。
一個網域名稱通常由www.somesite.com構成,但是可以隨意選擇要不要www部分。為了例子簡單,我們只允許.com,.net,和.org的網域名稱是在考慮之中的。我們最好這樣對Regex中的網域名稱部分表示如下:
(www.)?.+.(com|net|org)$
把所有的東西放在一起,我們的Regex就可以用作檢查一個網域名稱,如:
<?php
function isValidDomain($domainName)
{
return ereg("^(http|ftp)://(www.)?.+.(com|net|org)$", $domainName);
}
//真(true)
echo isValidDomain("http://www.somesite.com");
//真(true)
echo isValidDomain("ftp://somesite.com");
//假 (false)
echo isValidDomain("ftp://www.somesite.fr");
//假 (false)
echo isValidDomain("www.somesite.com");
?>
例子二
因為我居住在澳大利亞雪梨,讓我們檢查一個典型的澳大利亞國際電話號碼。澳大利亞國際電話號碼的格式如下:
+61x xxxx-xxxx
第一個x是區號,其它的都是電話號碼。檢查以'+61'開頭且緊跟一個在2到9之間的區號的電話號碼,我們用下面的Regex:
^+61[2-9][[:space:]]
注意,上面的搜尋模式把'+'字元用''轉義,以便於可以在搜尋中包含,不至於被解釋為一個Regex。[2-9]告訴Regex引擎我們需要包含一個2到9之間的數字。[[:space:]]類則告訴Regex期望在這裡有一個空白。
這裡是電話號碼剩下的搜尋模式:
[0-9]{4}-[0-9]{4}$
這裡沒有什麼不尋常的地方,我們只是告訴Regex引擎電話號碼可用的數字,它必須是4個數位組合,跟著一個串連符,再跟著另一個4個數位組合,然後一個字串尾部字元。
把完整的Regex放在一起,放進一個函數,我們可以用代碼來檢查一些澳大利亞國際電話號碼:
<?php
function isValidPhone($phoneNum)
{
echo ereg("^+61[2-9][[:space:]][0-9]{4}-[0-9]{4}$", $phoneNum);
}
// 真(true)
echo isValidPhone("+619 0000-0000");
// 假(false)
echo isValidPhone("+61 00000000");
//假( false)
echo isValidPhone("+611 00000000");
?>
總結
Regex用一些不適合書寫和重複的代碼來檢查一個字串。在最後的幾頁裡,我們已經講解了所有的Posix標準Regex的基礎,包括字元,分組和PHP ereg函數。我們也知道了怎麼用Regex來檢查一些PHP中簡單的字串。
譯者注釋:本人英文不怎麼好,可能一些地方有出入。本文中的字元類其實是我們所說的字元簇
經典Regex
Regex用於字串處理,表單驗證等場合,實用高效,但用到時總是不太把握,以致往往要上網查一番。我將一些常用的運算式收藏在這裡,作備忘之用。本貼隨時會更新。
匹配中文字元的Regex: [u4e00-u9fa5]
匹配雙位元組字元(包括漢字在內):[^x00-xff]
應用:計算字串的長度(一個雙位元組字元長度計2,ASCII字元計1)
String.prototype.len=function(){return this.replace([^x00-xff]/g,"aa").length;}
匹配空行的Regex:n[s| ]*r
匹配HTML標記的Regex:/<(.*)>.*</1>|<(.*) />/
匹配首尾空格的Regex:(^s*)|(s*$)
應用:javascript中沒有像vbscript那樣的trim函數,我們就可以利用這個運算式來實現,如下:
String.prototype.trim = function()
{
return this.replace(/(^s*)|(s*$)/g, "");
}
利用Regex分解和轉換IP地址:
下面是利用Regex匹配IP地址,並將IP地址轉換成對應數值的Javascript程式:
function IP2V(ip)
{
re=/(d+).(d+).(d+).(d+)/g //匹配IP地址的Regex
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
throw new Error("Not a valid IP address!")
}
}
不過上面的程式如果不用Regex,而直接用split函數來分解可能更簡單,程式如下:
var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))
匹配Email地址的Regex:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
匹配網址URL的Regex:http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?
利用Regex去除字串中重複的字元的演算法程式:
var s="abacabefgeeii"
var s1=s.replace(/(.).*1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2) //結果為:abcefgi
我原來在CSDN上發貼尋求一個運算式來實現去除重複字元的方法,最終沒有找到,這是我能想到的最簡單的實現方法。思路是使用後向引用取出包括重複的字元,再以重複的字元建立第二個運算式,取到不重複的字元,兩者串聯。這個方法對於字元順序有要求的字串可能不適用。
得用Regex從URL地址中提取檔案名稱的javascript程式,如下結果為page1
s="http://www.9499.net/page1.htm"
s=s.replace(/(.*/){0,}([^.]+).*/ig,"$2")
alert(s)
利用Regex限制網頁表單裡的文字框輸入內容:
用Regex限制只能輸入中文:onkeyup="value=value.replace(/[^u4E00-u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^u4E00-u9FA5]/g,''))"
用Regex限制只能輸入全形字元: onkeyup="value=value.replace(/[^uFF00-uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^uFF00-uFFFF]/g,''))"
用Regex限制只能輸入數字:onkeyup="value=value.replace(/[^d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^d]/g,''))"
用Regex限制只能輸入數字和英文:onkeyup="value=value.replace(/[W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^d]/g,''))"
如何用Regex來表示中文
由於中文的ASCII碼是有一定的範圍的。所以你可以用下面的Regex來表示中文。
/^[chr(0xa1)-chr(0xff)]+$/
下面是一個使用的例子:
$str = "超越PHP";
if (preg_match("/^[".chr(0xa1)."-".chr(0xff)."]+$/", $str)) {
echo "這是一個純中文字串";
} else {
echo "這不是一個純中文字串";
}
Regex
如果原來沒有使用過Regex,那麼可能對這個術語和概念會不太熟悉。不過,它們並不是您想象的那麼新奇。
請回想一下在硬碟上是如何尋找檔案的。您肯定會使用 ? 和 * 字元來協助尋找您正尋找的檔案。? 字元匹配檔案名稱中的單個字元,而 * 則匹配一個或多個字元。一個如 'data?.dat' 的模式可以找到下述檔案:
data1.dat
data2.dat
datax.dat
dataN.dat
如果使用 * 字元代替 ? 字元,則將擴大找到的檔案數量。'data*.dat' 可以匹配下述所有檔案名稱:
data.dat
data1.dat
data2.dat
data12.dat
datax.dat
dataXYZ.dat
儘管這種搜尋檔案的方法肯定很有用,但也十分有限。? 和 * 萬用字元的有限能力可以使你對Regex能做什麼有一個概念,不過Regex的功能更強大,也更靈活。
--------------------------------------------------------------------------------
2
早期起源
Regex的“祖先”可以一直上溯至對人類神經系統如何工作的早期研究。Warren McCulloch 和 Walter Pitts 這兩位神經生理學家研究出一種數學方式來描述這些神經網路。
1956 年, 一位叫 Stephen Kleene 的美國數學家在 McCulloch 和 Pitts 早期工作的基礎上,發表了一篇標題為“神經網事件的標記法”的論文,引入了Regex的概念。Regex就是用來描述他稱為“正則集的代數”的運算式,因此採用“Regex”這個術語。
隨後,發現可以將這一工作應用於使用Ken Thompson 的計算搜尋演算法的一些早期研究,Ken Thompson是Unix 的主要發明人。Regex的第一個實用應用程式就是 Unix 中的qed 編輯器。
如他們所說,剩下的就是眾所周知的曆史了。從那時起直至現在Regex都是基於文本的編輯器和搜尋工具中的一個重要部分。
--------------------------------------------------------------------------------
3.
使用Regex
在典型的搜尋和替換操作中,必須提供要尋找的確切文字。這種技術對於靜態文本中的簡單搜尋和替換任務可能足夠了,但是由於它缺乏靈活性,因此在搜尋動態文本時就有困難了,甚至是不可能的。
使用Regex,就可以:
測試字串的某個模式。例如,可以對一個輸入字串進行測試,看在該字串是否存在一個電話號碼模式或一個信用卡號碼模式。這稱為資料有效性驗證。
替換文本。可以在文檔中使用一個Regex來標識特定文字,然後可以全部將其刪除,或者替換為別的文字。
根據模式比對從字串中提取一個子字串。可以用來在文本或輸入欄位中尋找特定文字。
例如,如果需要搜尋整個 web 網站來刪除某些過時的材料並替換某些HTML 格式化標記,則可以使用Regex對每個檔案進行測試,看在該檔案中是否存在所要尋找的材料或 HTML 格式化標記。用這個方法,就可以將受影響的檔案範圍縮小到包含要刪除或更改的材料的那些檔案。然後可以使用Regex來刪除過時的材料,最後,可以再次使用Regex來尋找並替換那些需要替換的標記。
另一個說明Regex非常有用的樣本是一種其字串處理能力還不為人所知的語言。VBScript 是 Visual Basic 的一個子集,具有豐富的字串處理功能。與 C 類似的 Visual Basic Scripting Edition 則沒有這一能力。Regex給 Visual Basic Scripting Edition 的字串處理能力帶來了明顯改善。不過,可能還是在 VBScript 中使用Regex的效率更高,它允許在單個運算式中執行多個字串操作。
--------------------------------------------------------------------------------
Regex文法
一個Regex就是由一般字元(例如字元 a 到 z)以及特殊字元(稱為元字元)組成的文字模式。該模式描述在尋找文字主體時待匹配的一個或多個字串。Regex作為一個模板,將某個字元模式與所搜尋的字串進行匹配。
這裡有一些可能會遇到的Regex樣本:
Visual Basic Scripting Edition VBScript 匹配
/^[ t]*$/ "^[ t]*$" 匹配一個空白行。
/d{2}-d{5}/ "d{2}-d{5}" 驗證一個識別碼是否由一個2位元字,一個連字號以及一個5位元字組成。
/<(.*)>.*</1>/ "<(.*)>.*</1>" 匹配一個 HTML 標籤。
下表是元字元及其在Regex上下文中的行為的一個完整列表:
字元 描述
將下一個字元標記為一個特殊字元、或一個原義字元、或一個 後向引用、或一個八進位轉義符。例如,'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 集合,在Visual Basic Scripting Edition 中則使用 $0…$9 屬性。要匹配圓括弧字元,請使用 '(' 或 ')'。
(?:pattern) 匹配 pattern 但不擷取匹配結果,也就是說這是一個非擷取匹配,不進行儲存供以後使用。這在使用 "或" 字元 (|) 來組合一個模式的各個部分是很有用。例如, 'industr(?:y|ies) 就是一個比 'industry|industries' 更簡略的運算式。
(?=pattern) 正向預查,在任何匹配 pattern 的字串開始處匹配尋找字串。這是一個非擷取匹配,也就是說,該匹配不需要擷取供以後使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。預查不消耗字元,也就是說,在一個匹配發生後,在最後一次匹配之後立即開始下一次匹配的搜尋,而不是從包含預查的字元之後開始。
(?!pattern) 負向預查,在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字串開始處匹配尋找字串。這是一個非擷取匹配,也就是說,該匹配不需要擷取供以後使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "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 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'erb' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
B 匹配非單詞邊界。'erB' 能匹配 "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 匹配任何空白字元,包括空格、定位字元、換頁符等等。等價於 [ fnrtv]。
S 匹配任何非空白字元。等價於 [^ fnrtv]。
t 匹配一個定位字元。等價於 x09 和 cI。
v 匹配一個垂直定位字元。等價於 x0b 和 cK。
w 匹配包括底線的任何單詞字元。等價於'[A-Za-z0-9_]'。
W 匹配任何非單詞字元。等價於 '[^A-Za-z0-9_]'。
xn 匹配 n,其中 n 為十六進位轉義值。十六進位轉義值必須為確定的兩個數字長。例如, 'x41' 匹配 "A"。'x041' 則等價於 'x04' & "1"。Regex中可以使用 ASCII 編碼。.
num 匹配 num,其中 num 是一個正整數。對所擷取的匹配的引用。例如,'(.)1' 匹配兩個連續的相同字元。
n 標識一個八進位轉義值或一個後向引用。如果 n 之前至少 n 個擷取的子運算式,則 n 為後向引用。否則,如果 n 為八位元字 (0-7),則 n 為一個八進位轉義值。
nm 標識一個八進位轉義值或一個後向引用。如果 nm 之前至少有is preceded by at least 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 匹配著作權符號 (?)。
4.
建立Regex
構造Regex的方法和建立數學運算式的方法一樣。也就是用多種元字元與操作符將小的運算式結合在一起來建立更大的運算式。
可以通過在一對分隔字元之間放入運算式模式的各種組件來構造一個Regex。對 Visual Basic Scripting Edition 而言,分隔字元為一對正斜杠 (/) 字元。例如:
/expression/
對 VBScript 而言,則採用一對引號 ("") 來確定Regex的邊界。例如:
"expression"
在上面所示的兩個樣本中,Regex模式 (expression) 均儲存在RegExp 對象的Pattern 屬性中。
Regex的組件可以是單個的字元、字元集合、字元範圍、字元間的選擇或者所有這些組件的任意組合。
--------------------------------------------------------------------------------
5.
優先權順序
在構造Regex之後,就可以象數學運算式一樣來求值,也就是說,可以從左至右並按照一個優先權順序來求值。
下表從最高優先順序到最低優先順序列出各種Regex操作符的優先權順序:
操作符 描述
轉義符
(), (?, (?=), [] 圓括弧和方括弧
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, anymetacharacter 位置和順序
| “或”操作
--------------------------------------------------------------------------------
6.
一般字元
一般字元由所有那些未顯式指定為元字元的列印和非列印字元組成。這包括所有的大寫和小寫字母字元,所有數字,所有標點符號以及一些符號。
最簡單的Regex是一個單獨的一般字元,可以匹配所搜尋字串中的該字元本身。例如,單字元模式 'A' 可以匹配所搜尋字串中任何位置出現的字母 'A'。這裡有一些單字元Regex模式的樣本:
/a/
/7/
/M/
等價的 VBScript 單字元Regex為:
"a"
"7"
"M"
可以將多個單字元組合在一起得到一個較大的運算式。例如,下面的 Visual Basic Scripting Edition Regex不是別的,就是通過組合單字元運算式 'a'、'7'以及 'M' 所建立出來的一個運算式。
/a7M/
等價的 VBScript 運算式為:
"a7M"
請注意這裡沒有串連操作符。所需要做的就是將一個字元放在了另一個字元後面。
--------------------------------------------------------------------------------
特殊字元
有不少元字元在試圖對其進行匹配時需要進行特殊的處理。要匹配這些特殊字元,必須首先將這些字元轉義,也就是在前面使用一個反斜線 ()。下表給出了這些特殊字元及其含義:
特殊字元 說明
$ 匹配輸入字串的結尾位置。如果設定了 RegExp 對象的 Multiline 屬性,則 $ 也匹配 'n' 或 'r'。要匹配 $ 字元本身,請使用 $。
( ) 標記一個子運算式的開始和結束位置。子運算式可以擷取供以後使用。要匹配這些字元,請使用 ( 和 )。
* 匹配前面的子運算式零次或多次。要匹配 * 字元,請使用 *。
+ 匹配前面的子運算式一次或多次。要匹配 + 字元,請使用 +。
. 匹配除分行符號 n之外的任何單字元。要匹配 .,請使用 。
[ 標記一個中括號運算式的開始。要匹配 [,請使用 [。
? 匹配前面的子運算式零次或一次,或指明一個非貪婪限定符。要匹配 ? 字元,請使用 ?。
將下一個字元標記為或特殊字元、或原義字元、或後向引用、或八進位轉義符。例如, 'n' 匹配字元 'n'。'n' 匹配分行符號。序列 '' 匹配 "",而 '(' 則匹配 "("。
^ 匹配輸入字串的開始位置,除非在方括號運算式中使用,此時它表示不接受該字元集合。要匹配 ^ 字元本身,請使用 ^。
{ 標記限定符運算式的開始。要匹配 {,請使用 {。
| 指明兩項之間的一個選擇。要匹配 |,請使用 |。
--------------------------------------------------------------------------------