它們都把一個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.111cn.net構成,但是可以隨意選擇要不要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.111cn.net"); //真(true) echo isValidDomain("ftp://www.111cn.net"); //假 (false) echo isValidDomain("ftp://www.hzhuti.fr"); //假 (false) echo isValidDomain("www.111cn.net"); ?>
|
例子二
因為我居住在澳大利亞雪梨,讓我們檢查一個典型的澳大利亞國際電話號碼。澳大利亞國際電話號碼的格式如下:
+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: [u4e00-u9fa5]
評註:匹配中文還真是個頭疼的事,有了這個運算式就好辦了
匹配雙位元組字元(包括漢字在內):[^x00-xff]
評註:可以用來計算字串的長度(一個雙位元組字元長度計2,ASCII字元計1)
匹配空白行的Regex:ns*r
評註:可以用來刪除空白行
匹配HTML標記的Regex:< (S*?)[^>]*>.*?|< .*? />
評註:網上流傳的版本太糟糕,上面這個也僅僅能匹配部分,對於複雜的嵌套標記依舊無能為力
匹配首尾空白字元的Regex:^s*|s*$
評註:可以用來刪除行首行尾的空白字元(包括空格、定位字元、換頁符等等),非常有用的運算式
匹配Email地址的Regex:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
評註:表單驗證時很實用
匹配網址URL的Regex:[a-zA-z]+://[^s]*
評註:網上流傳的版本功能很有限,上面這個基本可以滿足需求
匹配帳號是否合法(字母開頭,允許5-16位元組,允許字母數字底線):^[a-zA-Z][a-zA-Z0-9_]$
評註:表單驗證時很實用
匹配國內電話號碼:d-d|d-d
評註:匹配形式如 0511-4405222 或 021-87888822
匹配騰訊QQ號:[1-9][0-9]
評註:騰訊QQ號從10000開始
匹配中國郵遞區號:[1-9]d(?!d)
評註:中國郵遞區號為6位元字
匹配身份證:d|d
評註:中國的身份證為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個英文字母或者底線組成的字串