正規表示式提供了一種處理文字的強大方法。使用正規表示式,您可以對使用者輸入進行複雜的檢驗、解析使用者輸入和檔案內容,以及重新格式化字串。 PHP 為使用者提供了使用 POSIX 和 PCRE 正規表示式的簡單方法。本教學將討論 POSIX 和 PCRE 之間的差異,並介紹如何使用正規表示式和 PHP V5。
開始之前
#了解透過本教學課程可學到哪些內容以及如何更好地利用本教程。
關於本教學
#正規表示式提供了處理文字的強大方法。使用正規表示式,您可以對使用者輸入進行複雜的檢驗、解析使用者輸入和文字內容,以及重新格式化字串。
目標
#本教學將集中介紹使用POSIX 和PCRE 正規表示式的簡單方法,使您熟練PHP 的正規表示式。我們將探討 POSIX 和 PCRE 之間的差異,也會介紹如何使用正規表示式和 PHP V5。透過學習本教程,您將了解使用正規表示式的方法、時機和理由。
系統需求
#您可以在任何安裝了PHP 的類別Microsoft® Windows® 或類UNIX® 系統(包括Mac OS X 和Linux®)上完成本教學。由於我們所介紹的內容均為 PHP 內建插件,因此只需在系統中安裝 PHP 即可,無需安裝其他軟體。
開始
#何為正規表示式?
幾年前,我對 Web 表單的輸入框做了一些有趣的檢驗。用戶將在此表單中輸入電話號碼。隨後,此電話號碼會以使用者鍵入的形式列印在使用者的廣告中。依照要求,美國的電話號碼可以用幾種方式輸入:可以是 (555) 555-5555,也可以是 555-555-5555,但不能接受 555-5555 這樣的形式。
您或許會感到奇怪,為什麼我們不拋開所有的非數字字符,只保證剩餘的字符總數為 10 呢?這種方法確實可行,但無法阻止使用者輸入 !555?333-3333 這樣的內容。
以 Web 開發者的眼光來看,這種情況帶來了一項有趣的挑戰。我可以編寫例程來檢查各種不同格式,但我希望能夠找到一種解決方案,假如用戶隨後認可 555.555.5555 這樣的格式,這種解決方案能具備一定的靈活性。
這正是正規表示式(簡稱 regex)的適用場景。之前我已經將它們剪切並粘貼到了應用程式中,但從未發現任何難以理解的語法問題。 Regex 看起來非常像數學表達式。當您看到一個形如 2x2=4 的表達式時,您通常會想到 “2 乘以 2 等於 4”。正規表示式與之非常類似。閱讀過本文後,當您看到一個這樣的正規表示式 ^b$ 時,您會告訴自己:「一行的開頭是 b,隨後就是行尾」。不僅如此,您還會意識到在 PHP 中使用正規表示式有多麼簡單。
使用regex 的時機
在有規則可循時,您應該使用regex 來完成搜尋和替換操作,但不必具有需要找到或替換的確切字元。舉例來說,在上文中提到的電話號碼的例子中,使用者定義了表明所輸入電話號碼的格式的規則,但並未定義電話號碼中所包含的數字。這同樣適用於有大批使用者輸入的場景。美國州名縮寫可限制為兩個從 A 到 Z 的大寫字母。這裡也可使用正規表示式,您可簡單地將表單中的文字或使用者輸入限制為字母表中的字母,而無需考慮大小寫和長度問題。
不宜使用 regex 的時機
正規表示式功能強大,但也有一些缺陷。其中之一就是要求具備讀寫表達式的相關技能。如果您決定在應用程式中包含正規表示式,則應該對其進行完整的註解。這樣,此後如果有其他人需要更改表達式,即可在不中斷功能的情況下完成更改。另外,如果您對於使用正規表示式不夠熟悉,可能會發現它們難於調試。
為避免這些難題,在更簡單的內建功能足以很好地解決問題時不要使用正規表示式。
POSIX 與PCRE
PHP 支援兩種正規表示式的實作:Portable Operating System Implementation(POSIX)和Perl-Compatible Regular Expression(PCRE) 。這兩種實作提供了不同的特性,但它們在 PHP 中使用起來一樣簡單。您所使用的 regex 風格取決於您過去在 regex 使用方面的經驗和使用習慣。有一些證據表明,PCRE 表達式的速度比 POSIX 表達式要略微快一點,但在絕大多數應用程式中,這一差異體現得不是那麼明顯。
在本文的範例中,各 regex 方法的語法都包含在註解中。在函數語法中,regex 為 regex
參數,所搜尋的字串為 string
。括號中的參數是可選的,由於本教學主要介紹基礎內容,故不會給予全部可選參數的介紹。
正規表示式語法
儘管POSIX 和PCRE 實作在某些特性和字元類別的支援方面有所不同,但它們的語法是相同的。每個正規表示式都是由一個或多個字元、特殊字元(有時也稱為元字元)、字元類別和字元組構成的。
POSIX 和 PCRE 使用相同的通配符 —— 在 regex 中以萬用字元來表示 「此處可為任意內容」。通配符字元為一個英文句號或點(.
)。若要尋找英文句號或點,可使用轉義字元 /
: /.
。下文中所討論的其他特殊字元也是如此,例如行錨(line anchor)和限定符。如果一個字元在正規表示式中有特殊意義,那麼就必須透過轉義來表達其原本的文字意義。
行錨 是特殊的元字符,與一行的開頭和結尾相匹配,但不會捕獲任何文字(參見表 1)。例如,如果某一行以字母 a
開頭,那麼表達式 ^a
中的行錨不會捕獲字母 a
,而是符合行的開頭。
表1. 行錨點
錨 | 描述 |
---|---|
^ |
#符合一行的開頭 |
$ |
#符合一行的結尾 |
限定符 套用於緊接於其前面的表達式(參見表2)。使用限定符,您可以指定在一次搜尋中查找到一個表達式的次數。例如,表達式 a+
將一次或多次地查找到字母 a
。
表2.限定符
# 描述 | |
---|---|
? |
限定符之前的表達式可被查找到0 次或1 次 |
+ |
限定符之前的表達式可被查找到1 次或多次 |
##*
| #限定符之前的表達式可被查找到任意次(含0 次)|
#{n}
| 限定符之前的表達式只可被查找到n 次|
{n,m}
| 限定符之前的表達式可被查找到n 次到m 次之間
表3. 分組與捕捉
|
POSIX 字元類別
POSIX 正規表示式遵循一些使其可為許多 regex 實作所使用的標準(請參閱表 4)。例如,如果您正在編寫一條 POSIX 正規表示式,您可以在 PHP 中使用它、可以透過 grep
指令使用它,也可以透過許多支援正規表示式的編輯器使用它。
表4. POSIX 字元類別
字元 | 描述 |
---|---|
[:alpha:] |
符合包含字母與數字的字元 |
#[:digit:] |
符合任意數字 |
#[:space:] |
符合任意空白 |
POSIX 符合
#有兩個使用POSIX 正規表示式搜尋字串的函數,即ereg()
和eregi()
。
ereg()
ereg()
方法為特定正規表示式搜尋字串。如果未找到任何符合項,則傳回0,因此您可以給出如下測試:
#清單1. ereg() 方法
<?php $phonenbr="555-555-5555"; // Syntax is ereg( regex, string [, out_captures_array]) if (ereg("[-[:digit:]]{12}", $phonenbr)) { print("Found match!/n"); } else { print("No match found!/n"); } ?> |
#正規表示式[-[:digit:]]{12}
尋找12 個為數字或連字符的字元。就處理電話號碼而言,這有些粗略,您也可將其改寫成這樣的形式:^[0-9]{3}-[0-9]{3}-[0-9]{ 4}$
。 (在regex 中,[0-9]
和[:digit:]
實際上是完全相同的,您可能更願意使用[0-9]
的形式,因為它更短。它會找出行的開頭(^
),後面接著一組3 個數字([0-9]{3}
)、一個連字號(-
)、另外一組3 個數字、另外一個連字號、一組4 個數字,然後是行的結尾($
#)。當您手動編寫表達式時,這會使您了解正規表示式要處理的問題的複雜程度如何,從而有助於預測使用表達式搜尋或替換的資料類型。
eregi()
eregi()
方法類似於ereg()
,不同之處在於它對大小寫不敏感。它將傳回一個包含所找到的符合項目長度的整數,但您很可能會將其用於條件語句中,如下所示:
清單2. eregi () 方法
<?php $str="Hello World!"; // Syntax is ereg( regex, string [, out_captures_array]) if (eregi("hello", $str)) { print("Found match!/n"); } else { print("No match found!/n"); } ?> |
#執行此範例時,將輸出Found match!
,這是因為在忽略大小寫的搜尋中找到了hello。如果您使用的是 ereg
,搜尋將會失敗。
POSIX 替換
#ereg_replace()
和eregi_replace()
這兩種方法用於在文字中進行替換,具有POSIX 正規表示式的特性。
ereg_replace()
您可以使用 ereg_replace()
方法以 POSIX 正規表示式語法進行大小寫敏感的取代。如下範例描述如何取代帶有超連結的字串內的電子郵件地址:
#清單3. ereg_replace() 方法
<?php $origstr = "My e-mail address is: first.last@example.com"; // Syntax is: ereg_replace( regex, replacestr, string ) $newstr = / ereg_replace("([.[:alpha:][:digit:]]+@[.[:alpha:][:digit:]]+)", "<a href=/"mailto://1/">//1</a>", $origstr); print("$newstr/n"); ?> |
#這是一個用於匹配電子郵件地址的正規表示式的不完整版本,但它展示了與str_replace()
等其他普通替換函數相比,ereg_replace()
的強大之處。使用正規表示式時,您可定義搜尋的規則,而不是搜尋文字字元。
eregi_replace()
除忽略大小寫之外,eregi_replace()
函數與ereg_replace()
是完全相同的:
清單4. eregi_replace() 函數
<?php $origstr = "1 BANANA, 2 banana, 3 Banana"; // Syntax is: eregi_replace( regex, replacestr, string ) $newstr = eregi_replace("banana", "pear", $origstr); print("New string is: '$newstr'/n"); ?> |
banana
替换为 pear
,替换操作忽略了大小写。PCRE 字符类
由于 PCRE 语法支持更短的字符类和更多的特性,因此它比 POSIX 语法更为强大。表 5 列出了 PCRE 中支持而在 POSIX 表达式中没有的部分字符类。
表 5. PCRE 字符类
字符类 | 描述 |
---|---|
/b |
词边界,查找词的开始和结尾 |
/d |
匹配任意数字 |
/s |
匹配任意空白,如 tab 或空格 |
/t |
匹配一个 tab 字符 |
/w |
匹配包含字母与数字的字符 |
PCRE 匹配
PHP 中的 PCRE 匹配函数与 POSIX 匹配函数类似,但如果您习惯使用 POSIX 表达式,那么 PCRE 匹配函数的一项特性可能会使您感到棘手:PCRE 函数要求表达式以分隔符开始和结束。在绝大多数示例中,分隔符都是一个 /
,可在引号内表达式的开始和结尾处看到。务必牢记,此分隔符并非表达式的一部分。
在 PCRE 中的最后一个分隔符后,您可添加一个修饰符来更改正则表达式的行为。举例来说,i
修饰符使正则表达式对大小写不敏感。这是与 POSIX 方法的一项重要差异,在 POSIX 中,您需要按照对大小写敏感性的需求来调用不同的方法。
preg_grep()
preg_grep()
方法返回一个数组,其中包含通过正则表达式在其中找到匹配项的另外一个数组的全部项目。如果您有一个较大的值集,并希望对其进行搜索以查找匹配项,那么该方法非常有用。下面是一个示例:
清单 5. preg_grep() 方法
<?php $array = array( "1", "3", "ABC", "XYZ", "42" ); // Syntax is preg_grep( regex, inputarray ); $grep_array = preg_grep("/^/d+$/", $array); print_r($grep_array); ?> |
在本例中,正则表达式 ^/d+$
查找行的开始(^
)和结尾($
)之间包含一个或多个数字(/d+
)的数组的所有元素。
preg_match()
preg_match()
函数使用 PCRE 在字符串中查找匹配项,它需要两个参数:regex 和字符串。您可以选择提供一个将由匹配项填充的数组、允许您修改匹配操作行为的标志,还可提供字符串中开始查找匹配项的位置(offset
)。示例如下:
清单 6. offset 方法
<?php $string = "abcdefgh"; $regex = "/^[a-z]+$/i"; // Syntax is preg_match(regex, string, [, out_matches [, flags [, offset]]]); if (preg_match($regex, $string)) { printf("Pattern '%s' found in string '%s'/n", $regex, $string); } else { printf("No match found in string '%s'!/n", $string); } ?> |
本例使用了正则表达式 ^[a-z]+$
,在行的开始(^
)和结尾($
)之间搜索可查找到一次或多次的([a-z]+
)、从 a
到 z
的任意字母。
preg_match_all()
preg_match_all()
函数为在字符串中查找到的全部匹配项构建一个数组。下例构建了一个包含句子中全部词的数组:
清单 7. preg_match_all() 函数
<?php $string = "The quick red fox jumped over the lazy brown dog"; $re = "//b/w+/b/"; // Syntax is preg_match_all( regex, string, return_array [, flags [, offset]]) preg_match_all($re, $string, $arrayout); print_r($arrayout); ?> |
正则表达式 /b/w+/b
在词边界(/b
)间查找可找到一次或多次的(/w+
)单词字符。每个词都将置入输出数组 $arrayout
的一个数组元素中。
PCRE 替换
在 PHP 中进行 PCRE 替换与 POSIX 替换类似,不同之处在于使用的是 preg_replace()
而非 ereg_replace()
和 eregi_replace()
。
preg_replace()
preg_replace()
函数使用 PCRE 进行替换。它需要这样几个参数:正则表达式、替换表达式和原始字符串。您还可以选择提供希望的最大替换数,以及以所完成的替换数填充的变量。示例如下:
清单 8. preg_replace() 函数
<?php $orig_string = "5555555555"; printf("Original string is '%s'/n", $orig_string); $re = "/^(/d{3})(/d{3})(/d{4})$/"; // Syntax is preg_replace( regex, replacement, string / [, limit [, out_count]] ); $new_string = preg_replace($re, "(//1) //2-//3", $orig_string); printf("New string is '%s'/n", $new_string); ?> |
本範例快速示範了擷取部分文字及使用反向引用 的方法,如 //1
。這些反向引用會插入圓括號內所符合的任意文字中,在本例中,//1
符合第 1 組 (/d{3})
。
在範例中,您可使用substr
將電話號碼分割開來,而對字串只需進行少量更改,要依靠substr
來可靠地捕獲正確文字會更加困難。
如果字串的形式可為(555)5555555
,您可將表達式修改為^(?(/d{3}))?(/d{ 3})(/d{4})$
以找出任一圓括號。
結束語
PHP 為正規表示式提供了兩種語法:POSIX 和 PCRE。本教程對 PHP 中支援 POSIX 和 PCRE 正規表示式的主要函數進行了高度概述。
使用正規表示式,您可以定義規則來進行更強大的搜尋和取代操作 —— 大大超越了文字的搜尋與取代。
參考資料
#學習
##您可以參考本文在developerWorks 全球網站上的
英文原文
。
Regular-Expressions.info
# 提供了關於正規表示式的相關資訊。
PHP: Regular Expression Functions (Perl-Compatible) - Manual
是涵蓋了 PCRE 相關內容的 PHP 線上文件。
Regular Expression Functions (POSIX Extended)
是關於 POSIX 的 PHP 線上文件。
存取 developerWorks 中的 PHP 專案資源 可取得更多關於 PHP 的資訊。
」、第2 部分 和
第3 部分
。
了解最新的
developerWorks 技術活動和 Webcast
。
存取developerWorks 開放原始碼專區
,獲得大量的how-to 資訊、工具和專案更新,幫助您使用開放原始碼技術進行開發並與IBM 產品結合使用。
取得產品與技術
透過 PHP.net 下載
最新版本的PHP
。
Regular Expression Library
有一個正規表示式的大型儲存庫。
訂購免費的SEK for Linux
使用
#透過參與developerWorks blogs
加入developerWorks 社區。關於作者 | ||
# |
|
以上是正規表示式在PHP中的正確使用方法指導的詳細內容。更多資訊請關注PHP中文網其他相關文章!