Regex提供了一種處理文本的強大方法。使用Regex,您可以對使用者輸入進行複雜的檢驗、解析使用者輸入和檔案內容,以及重新格式化字串。PHP 為使用者提供了使用 POSIX 和 PCRE Regex的簡單方法。本教程將討論 POSIX 和 PCRE 之間的差異,並介紹如何使用Regex和 PHP V5。
開始之前
瞭解通過本教程可學到哪些內容以及如何更好地利用本教程。
關於本教程
Regex提供了一種處理文本的強大方法。使用Regex,您可以對使用者輸入進行複雜的檢驗、解析使用者輸入和常值內容,以及重新格式化字串。
目標
本教程將集中介紹使用 POSIX 和 PCRE Regex的簡單方法,使您熟練掌握 PHP 的Regex。我們將探討 POSIX 和 PCRE 之間的差異,還會介紹如何使用Regex和 PHP V5。通過學習本教程,您將瞭解使用Regex的方法、時機和理由。
系統需求
您可以在任何安裝了 PHP 的類 Microsoft Windows 或類 UNIX 系統(包括 Mac OS X 和 Linux)上完成本教程。由於我們所介紹的內容均為 PHP 內建外掛程式,因此只需在系統中安裝 PHP 即可,無需安裝其他軟體。
開始
何為Regex?
幾年前,我對 Web 表單的輸入框做了一些有趣的檢驗。使用者將在此表單中輸入電話號碼。隨後,此電話號碼會按使用者鍵入的形式列印在使用者的廣告中。按照要求,美國的電話號碼可以幾種方式輸入:可以是 (555) 555-5555,也可以是 555-555-5555,但不能接受 555-5555 這樣的形式。
您或許會感到奇怪,為什麼我們不拋開所有的非數字字元,只保證剩餘的字元總數為 10 呢?這種方法確實可行,但無法阻止使用者輸入 !555?333-3333 這樣的內容。
以一名 Web 開發人員的眼光來看,這種情況帶來了一項有趣的挑戰。我可以編寫常式來檢查各種不同格式,但我希望能夠找到一種解決方案,假如使用者隨後認可 555.555.5555 這樣的格式,這種解決方案能具備一定的靈活性。
這正是Regex(簡稱為 regex)的適用情境。之前我已經將它們剪下並粘貼到了應用程式中,但從未發現任何難以理解的文法問題。Regex 看上去非常像數學運算式。當您看到一個形如 2x2=4 的運算式時,您通常會想到 “2 乘以 2 等於 4”。Regex與之非常類似。閱讀過本文後,當您看到一個這樣的Regex ^b$ 時,您就會告訴自己:“一行的開頭是 b,隨後就是行尾”。不僅如此,您還會意識到在 PHP 中使用Regex有多麼簡單。
使用 regex 的時機
在有規則可循時,您應使用 regex 來完成搜尋和替換操作,但不必具有需要找到或替換的確切字元。舉例來說,在上文中提到的電話號碼的例子中,使用者定義了表明所輸入電話號碼的格式的規則,但並未定義電話號碼中所包含的數字。這同樣適用於有大批使用者輸入的情境。美國州名縮寫可限制為兩個從 A 到 Z 的大寫字母。這裡也可使用Regex,您可簡單地將表單中的文本或使用者輸入限制為字母表中的字母,而無需考慮大小寫和長度問題。
不宜使用 regex 的時機
Regex功能強大,但也有一些缺陷。其中之一就是要求具備讀寫運算式的相關技能。如果您決定在應用程式中包含Regex,就應該對其進行完整的注釋。這樣,此後如果有其他人需要更改運算式,即可在不中斷功能的情況下完成更改。另外,如果您對於使用Regex不夠熟悉,可能會發現它們難於調試。
為避免出現這些難題,在更簡單的內建功能足以很好地解決問題時不要使用Regex。
POSIX 與 PCRE
PHP 支援兩種Regex的實現:Portable Operating System Implementation(POSIX)和 Perl-Compatible Regular Expression(PCRE)。這兩種實現提供了不同的特性,但它們在 PHP 中使用起來一樣簡單。您所使用的 regex 風格取決於您過去在 regex 使用方面的經驗和使用習慣。有一些證據表明,PCRE 運算式的速度比 POSIX 運算式要略微快一點,但在絕大多數應用程式中,這一差別體現得不是那麼明顯。
在本文的樣本中,各 regex 方法的文法都包含在注釋中。在函數文法中,regex 為 regex
參數,所搜尋的字串為 string
。括弧中的參數是可選的,由於本教程主要介紹基礎內容,故不會給出全部選擇性參數的介紹。
Regex文法
儘管 POSIX 和 PCRE 實現在對某些特性和字元類的支援方面有所不同,但它們的文法是相同的。每個Regex都是由一個或多個字元、特殊字元(有時也稱為元字元)、字元類和字元組構成的。
POSIX 和 PCRE 使用相同的萬用字元 —— 在 regex 中以萬用字元來表示 “此處可為任意內容”。萬用字元字元為一個英文句號或點(.
)。若要尋找英文句號或點,可使用逸出字元 /
: /.
。下文中所討論的其他特殊字元也是如此,例如行錨(line anchor)和限定符。如果一個字元在Regex中有特殊含義,那麼必須通過轉義才能表達其原本的文字含義。
行錨 是特殊的元字元,與一行的開頭和結尾相匹配,但不會捕獲任何文本(參見表 1)。例如,如果某一行以字母 a
開頭,那麼運算式 ^a
中的行錨不會捕獲字母 a
,而是匹配行的開頭。
表 1. 行錨
限定符 應用於緊接於其前的運算式(參見表 2)。使用限定符,您可以指定在一次搜尋中尋找到一個運算式的次數。例如,運算式 a+
將一次或多次地尋找到字母 a
。
表 2. 限定符
限定符 |
描述 |
? |
限定符之前的運算式可被尋找到 0 次或 1 次 |
+ |
限定符之前的運算式可被尋找到 1 次或多次 |
* |
限定符之前的運算式可被尋找到任意次(含 0 次) |
{n} |
限定符之前的運算式僅可被尋找到 n 次 |
{n,m} |
限定符之前的運算式可被尋找到 n 次到 m 次之間 |
在 regex 中,捕獲文本並在替換和搜尋操作中引用該文本是一項非常有用的特性(參見表 3)。通過使用捕獲功能,您可以執行搜尋,來尋找重複的單詞和閉合的 HTML 及 XML 標記。如果您在替換時使用了捕獲功能,那麼可以將找回的文本置入替換字串內。後面將給出一個樣本,展示如何以超連結替換電子郵件地址。
表 3. 分組與捕獲
POSIX 字元類
POSIX Regex遵循一些使其可為許多 regex 實現所用的標準(參見表 4)。例如,如果您正在編寫一條 POSIX Regex,您可以在 PHP 中使用它、可以通過 grep
命令使用它,也可以通過許多支援Regex的編輯器使用它。
表 4. POSIX 字元類
字元 |
描述 |
[:alpha:] |
匹配包含字母與數位字元 |
[:digit:] |
匹配任一數字 |
[:space:] |
匹配任意空白 |
POSIX 匹配
有兩個使用 POSIX Regex搜尋字串的函數,即 ereg()
和 eregi()
。
ereg()
ereg()
方法為特定Regex搜尋字串。如果未找到任何匹配項,則返回 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");}?> |
Regex [-[:digit:]]{12}
尋找 12 個為數字或連字號的字元。就處理電話號碼而言,這有些粗略,您也可將其改寫成這樣的形式:^[0-9]{3}-[0-9]{3}-[0-9]{4}$
。(在 regex 中,[0-9]
和 [:digit:]
實際上是完全相同的,您可能更願意使用 [0-9]
的形式,因為它更短些。)這種作為替代方案的運算式顯然更為精確。它會尋找行的開頭(^
),後接一組 3 個數字([0-9]{3}
)、一個連字號(-
)、另外一組 3 個數字、另外一個連字號、一組 4 個數字,然後是行的結尾($
)。當您手工編寫運算式時,這會使您瞭解到Regex要處理的問題的複雜程度如何,從而有助於預測出使用運算式搜尋或替換的資料類型。
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 Regex的特性。
ereg_replace()
您可以使用 ereg_replace()
方法以 POSIX Regex文法進行大小寫敏感的替換。如下樣本描述了如何替換帶有超連結的字串內的電子郵件地址:
清單 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");?> |
這是一條用於匹配電子郵件地址的Regex的不完整版本,但它展示了與 str_replace()
等其他普通替換函數相比,ereg_replace()
的強大之處。在使用Regex時,您可定義搜尋的規則,而不是搜尋文字字元。
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 中的最後一個分隔字元後,您可添加一個修飾符來更改Regex的行為。舉例來說,i
修飾符使Regex對大小寫不敏感。這是與 POSIX 方法的一項重要差異,在 POSIX 中,您需要按照對大小寫敏感性的需求來調用不同的方法。
preg_grep()
preg_grep()
方法返回一個數組,其中包含通過Regex在其中找到匹配項的另外一個數組的全部項目。如果您有一個較大的值集,並希望對其進行搜尋以尋找匹配項,那麼該方法非常有用。下面是一個樣本:
清單 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);?> |
在本例中,Regex ^/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);}?> |
本例使用了Regex ^[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);?> |
Regex /b/w+/b
在詞邊界(/b
)間尋找可找到一次或多次的(/w+
)單詞字元。每個詞都將置入輸出數組 $arrayout
的一個數組元素中。
PCRE 替換
在 PHP 中進行 PCRE 替換與 POSIX 替換類似,不同之處在於使用的是 preg_replace()
而非 ereg_replace()
和 eregi_replace()
。
preg_replace()
preg_replace()
函數使用 PCRE 進行替換。它需要這樣幾個參數:Regex、替換運算式和原始字串。您還可以選擇提供希望的最大替換數,以及以所完成的替換數填充的變數。樣本如下:
清單 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 為Regex提供了兩種文法:POSIX 和 PCRE。本教程對 PHP 中支援 POSIX 和 PCRE Regex的主要函數進行了高度概述。
使用Regex,您可以定義規則來進行更強大的搜尋和替換操作 —— 大大超越了文字的搜尋與替換。
參考資料
學習
您可以參閱本文在 developerWorks 全球網站上的 英文原文。
Regular-Expressions.info 提供了關於Regex的相關資訊。
PHP: Regular Expression Functions (Perl-Compatible) - Manual 是涵蓋了 PCRE 相關內容的 PHP 線上文檔。
Regular Expression Functions (POSIX Extended) 是關於 POSIX 的 PHP 線上文檔。
訪問 developerWorks 中的 PHP 項目資源 可擷取更多關於 PHP 的資訊。
關於學習使用 PHP 編程的 developerWorks 教程,請參閱 “學習 PHP,第 1 部分” 、第 2 部分 和 第 3 部分。
瞭解最新的 developerWorks 技術活動和 Webcast。
訪問 developerWorks 開放源碼專區,獲得大量的 how-to 資訊、工具和項目更新,協助您使用開放源碼技術進行開發並與 IBM 產品結合使用。
獲得產品和技術
通過 PHP.net 下載 最新版本的 PHP。
Regular Expression Library 有一個Regex的大型存放庫。
訂購免費的 SEK for Linux,這是兩張 DVD,其中包含了 IBM 在 Linux 平台上的最新試用軟體,包括 DB2、Lotus、Rational、Tivoli 和 WebSphere。
使用 IBM 試用軟體 革新您的下一個開放源碼項目,可通過下載或從 DVD 中獲得這些軟體。
討論
關於作者
|
|
|
Nathan A. Good 是明尼蘇達州 Twin Cities 的一位作者、軟體工程師和系統管理員。他撰寫的圖書包括與 Lee Babin 等人合著的 PHP 5 Recipes: A Problem-Solution Approach(Apress 出版社,2005 年)、Regular Expression Recipes for Windows Developers: A Problem-Solution Approach(Apress 出版社,2005 年)、Regular Expressions: A Problem-Solution Approach(Apress 出版社,2005 年)及與 Kapil Sharma 等人合著的 Professional Red Hat Enterprise Linux 3(Wrox 出版社,2004 年)。 |