PHP基礎系列之正則表達式(一)

正則表達式的定義

正則表達式就是描述字符排列模式的一種自定義的語法規則。
因爲正則表達式自己具備一套很是完整的、能夠編寫模式的語法體系,提供了一種靈活且直觀的字符串處理方法,故正則表達式也稱爲模式表達式。web

正則表達式的特色

  1. 正則表達式並非PHP特有的,JavaScript、Java、Perl、MySQL中均可以應用到正則表達式。
  2. 正則表達式經過構建具備特定規則的模式,與輸入的字符串信息進行比較,從而實現字符串的匹配、查找、替換及分割等操做。

例子:正則表達式

"/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i"                // 匹配網址URL的正則表達式
"/<(\S*?)[^>]*>.*?<\/\\1>|<.*?/>/i"                                // 匹配HTML標記的正則表達式
"/\w+([-+.]\w)+*@\w+([-.]\w+)*\.\w+([-.]\w+)*/"                    // 匹配E-mail地址的正則表達式
"/^<img\s+[^>]*\s*src\s*=\s*([']?)(?<url>\S+)'?[^>l ]*>$/"        // 匹配img標籤

注意

  1. 正則表達式是由普通字符與具備特殊意義的字符組成字符串
  2. 效率問題,若是可使用字符串函數完成任務,那儘可能不使用正則表達式函數
  3. 對於一些複雜操做,如表單驗證,計算髮布文章中有多少個句子,抓取網頁中某種格式的句子等使用正則表達式以及相關函數可以更好地實現
  4. 正則表達式具備必定的編寫規則(語義),是一種模式
  5. 若正則表達式不與 相應的正則表達式函數使用,那麼正則表達式僅僅是字符串,要達成匹配、查找、替換以及分割功能,正則表達式必須與相應的正則表達式函數配套使用。

PHP中兩套支持正則表達式的函數庫

  1. PCRE(Perl Compatible Regular Expression)

    由PCRE庫提供,與Perl語言兼容的正則表達式、使用"preg_"爲前綴命名的函數,並且表達式都應被包含在定界符中。sql

  2. POSIX(Portable Operation System Interface)

    自從PHP5.3.0開始廢棄.使用一套"ereg_"爲前綴命名的函數。jsp

注意

1. 兩套 函數庫功能相似,執行效率不一樣,通常來講,實現相同功能,使用 PCRE庫提供的正則表達式效率略佔優點。
2. PCRE 函數須要模式以定界符閉合。
3. 不像POSIX,PCRE 擴展沒有專門用於大小寫不敏感匹配的函數。
   取而代之的是,支持使用i (PCRE_CASELESS) 模式修飾符完成一樣的工做。 其餘模式修飾符一樣可用於改變匹配策略。
4. POSIX 函數從最左面開始尋找最長的匹配,可是 PCRE 在第一個合法匹配後中止。若是字符串 
   不匹配這沒有什麼區別,可是若是匹配,二者在結果和速度上都會有差異。 爲了說明
   這個不一樣, 考慮下面的例子(來自Jeffrey Friedl 的《精通正則表達式》一書)。 
   使用模式 one(self)?(selfsufficient)? 在字符串oneselfsufficient 上匹配,
   PCRE 會匹配到oneself,可是使用 POSIX,結果將是整個字符串 oneselfsufficient。
   兩個子串都匹配原始字符串,可是 POSIX 將 最長的做爲結果。

函數對比

POSIX PCRE
ereg_replace() preg_replace()
ereg() preg_match()
eregi_replace() preg_replace()
eregi() preg_match()
split() preg_split()
spliti() preg_split()
sql_regcase() 無對等函數
可參照PHP Manual

正則表達式語法規則

正則表達式描述了一種字符串的匹配模式,經過這個模式在特定的函數中對字符串進行匹配、查找、替換以及分割等操做。函數

正則表達式做爲一個匹配的模板,是由定界符,原子(普通字符,例如a-z)、有特殊功能的字符(稱爲元字符,例如*、+、?等),
以及模式修正符等部分組成的文字模式。this

例子:編碼

"/^https?\/\/(([a-zA-Z0-9_-])+(\.)?)*(\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i/"url

或者code

'/^<a.*?(?|\\t|\\r|\\n)?href=[\'"]?(.+?)[\'"]?(?(?|\\t|\\r|\\n)+.*?)?>(.+?)<\/a.*?>$/sim'server

1. 定界符中使用的是兩個斜線"/",將模式放在它之間說明。
2. 原子用到了<、a、href、=、'、"、/、>等普通字符和\t、\r、\n等轉義字符。
3. 元字符使用了[]、()、|、.、?、*、+等具備特殊含義的字符。
4. 用到模式修正符是在定界符最後一個斜線以後的三個字符"s"、"i"、"m"。

定界符

定界符爲PCRE不一樣於POSIX的特色之一。

除了字母、數字和反斜線之外的字符皆可爲定界符

例子:
{ }# #||!!

/<\/w+>/              // 使用反斜線做爲定界符號 合法
|(\d{3})-\d+|Sm     // 使用豎線"|"做爲定界符號 合法
!^(?i)PHP[34]!        // 使用感嘆號"!"做爲定界符 合法
{^\s+(\s+)?$}       // 使用花括號"{}"做爲定界符號 合法
/href='(.*)'        // 非法定界符,缺乏結束定界符
1-\d3-\d3-\d4/      // 非法定界符號,缺乏起始定界符

注意

若是沒有特殊要求,通常使用//做爲定界符

定界符是成對的,有開始符號,也有結束符號。

原子

原子是正則表達式中最基本的組成單位,並且在每一個模式中最少要包含一個原子。

原子是全部那些未顯式指定爲元字符的打印(能夠在屏幕上輸出的字符)和非打印字符(看不到的)組成的。
簡單地說,可以在正則表達式中單獨使用的字符就可稱爲原子

將其劃分爲五類

1. 普通字符做爲原子

普通字符是編寫正則表達式時最多見的原子,包括全部的大寫和小寫字母字符、全部數字等,例如:a-z、A-Z、0-9。

2. 非打印字符(看不到的)做爲原子

原子字符                    \x09 含義描述

+ \cx        匹配由x指明的控制符。
             例如,\c\M匹配一個Control-M或者回車符。x的值必須爲A-Z或a-z之一,
             不然,將c視爲一個原義的'c'字符。

+ \f         匹配一個換頁符。等價於\x0c和\cL

+ \n         匹配一個換行符。等價於\x0a和\cJ

+ \r         匹配一個回車符。等價於\x0d和\cM

+ \t         匹配一個製表符。等價於\x09和\cI

+ \v         匹配一個垂直製表符。等價於\x0b和\cK

3.通用字符類型作爲原子

前面介紹的原子,都是一個原子只能匹配一個字符。可是有時候咱們須要一個原子能夠匹配一類字符。

在正則表達式中能夠直接使用一些表明範圍的原子

原子字符            含義描述

\d                     匹配任意一個十進制數字,等價於[0-9]

\D                    匹配任意一個除十進制數字之外的字符,等價於[^0-9]

\w                    匹配任意一個數字、字母、下劃線,等價於[0-9a-zA-Z_]

\W                    匹配除數字、字母、下劃線之外的任意一個字符,等價於[^0-9a-zA-Z_]

\s                    匹配任意一個空白字符,等價於[\f\t\n\v\r]

\S                    匹配除空白字符之外的任何一個字符,等價於[^\t\f\n\v\r]

4. 自定義原子表([])做爲原子

有些時候,上面六個通用字符並不能知足咱們的需求,咱們須要自定義一類原子,好比說奇數(1,3,5,7,9).
因此這時候須要咱們自定義一類原子(稱之爲類原子),使用原子表"[]"就能夠定義一組彼此平等的原子,
只能匹配原子表中一個字符。

1表示匹配除表內原子外的任意字符,一般稱之爲排除原子表。

此外,在原子表中可使用連字符(-)鏈接一組按照ASCII碼順序排列的原子,可簡化書寫。

例子

'/[apj]sp/'             //  能夠匹配asp、jsp、PSP三種,從原子表中僅選擇一個做爲原子
'/[^apj]sp/'            //   能夠匹配除了asp、PSP、jsp三種之外的字符串,如xsp,ysp,zsp等
'/0[xX][0-9a-fA-F]/'    //   能夠匹配一個簡單的十六進制數,如0x2f、0X3AE或0x4aB等

5. 一些特殊字符和元字符做爲原子

在正則表達式中,任何一個符號均可以做爲原子使用,但若是該符號在正則表達式中有特殊意義,可使用轉義字符""
取消它的特殊意義,做爲一個普通字符使用。 如 "\. \* \+ \? \( \<\>"

" "轉義字符能夠將有意義的字符轉成沒意義的字符,還能夠將沒意義的字符轉爲有意義的字符

元字符

元字符是用於構建正則表達式的具備特殊含義的字符,如"*"、"+"、"?"等。

元字符不能單獨出現,必須用來修飾原子。

若是要在正則表達式中使用包含元字符自己,爲了使其失去特殊含義,則必須在前面加上""進行轉義

正則表達式的元字符

元字符                含義描述

\*                     匹配0次,1次或者屢次其前面的原子

\+                   匹配1次或屢次其前的原子

?                   匹配0次或者1次其前的原子

.                  匹配除了換行符外的任意一個字符

|                   匹配兩個或多個分支選擇

{n}                表示其前面的原子剛好出現n次

{n,}               表示其前面的原子出現很多於n次

{n,m}              表示其前面的原子至少出現n次,最多出現n次

^或\A              匹配輸入字符串的開始位置(或在多行模式下行的開頭,即緊隨一換行符後)

$或\Z              匹配輸入字符串(或者在多行模式下行的結尾,即緊隨一換行符後)

\b                 匹配單詞的邊界

\B                 匹配除單詞邊界之外的部分

[]                 匹配方括號中指定的任意一個原子

[^]                匹配除方括號中的原子之外的任意一個字符,排除原子表

()                 匹配其總體爲一個原子,即模式單元。能夠理解爲由多個單個原子組成的大原子

\xn                   匹配n,其中n爲十六進制轉義值。十六進制轉義值必須爲肯定的兩個數字長。
                   例如,`"\x41"`匹配`A`。`"\x041"`則等價於`"\x04&1"`。正則表達式中可使用ASCII編碼。

1. 限定符

限定符用來指定正則表達式的一個給定原子必需要出現多少次才能知足匹配。

總共有"*""+""?""{n}""{n,}""{n,m}"六種限定符,他們之間的區別主要是重複匹配的次數不一樣。

其中"*""+""{n,}"限定符是貪婪的,由於它們會盡量地匹配文字。

注意

元字符 "*" 表示0次、1次或屢次匹配其前的原子,也可使用"{0,}"完成一樣的匹配

"+"可使用"{1,}"表示,

"?"可使用"{0,1}"表示。

2. 邊界限制(斷言)

用來限定字符串或單詞的邊界範圍,以便得到更準確的匹配結果。元字符"^"("\A")"$"("\Z")
分別指字符串的開始於結束,而"\b"用於描述字符串中每一個單詞的前或者後邊界,
與之相反的元字符"\B"表示非單詞邊界。

例如:

有一個字符串 "this is a test", 使用邊界限制以下:

"/^this/"                匹配此字符是否以字符串"this"開始的,匹配成功

"/test$/"                匹配此字符是否以字符串"test"結束的,匹配成功

"/\bis\b/"                匹配此字符串中是否含有單詞"is",由於在字符串"is"兩邊都須要有邊界

"/\Bis\b/"                查找字符串"is"時,左邊不能有邊界而右邊必須右邊界,如"this"匹配成功

3. 句號(.)

在字符類之外,模式中的圓點能夠匹配目標中的任何一個字符,包括不可打印字符。但不匹配換行符號(默認狀況下),至關於"[^\n]"(UNIX系統)或者"[^\n\r]"(Windows系統)。

可是若是設定了模式修正符"s",則圓點也會匹配換行符。

處理圓點與處理^和$是徹底獨立的,惟一的聯繫是涉及換行

注意

1. `".*?"`或者`".+?"`組合來匹配除換行符之外的任何字符串。
   例如:`"/<b>.*?<\/b>/"`能夠匹配以`<b>`,`</b>`標籤開始於結束的任何不包括換行符的任意字符串

4. 模式選擇符(|)

豎線字符"|"用來分隔多選一模式,在正則表達式中匹配兩個或更多的選擇之一。

|的優先級是最低的,因此應在最後考慮其功能。

例如:LAMP | J2EE表示匹配LAMP,也能夠匹配J2EE,因爲|其優先級最低,因此並
不表示匹配'LAMP2EE'或者'LAMJEE'

也能夠像這樣使用 "/Linux|Apache|MySQL|PHP/",表示能夠從中任意匹配一組。

5. 模式單元

模式單元是使用元字符"()"將多個原子組成大原子使用。

一個模式單元中的表達式將被優先匹配

例子:

'/(very)*good/' //能夠匹配good、very good、very very good

6. 後向引用

後向引用是一個正則表達式中一個重要的應用點。

使用()標記的開始和結束的多個原子,不只僅是一個獨立的單元,也是一個子表達式(也稱之爲子模式)。

**對於一個正則表達式模式或部分模式兩邊添加圓括號將致使相關匹配存儲到一個臨時緩衝區中,能夠被捕獲供

之後使用**。

所捕獲的每一個子匹配都按照正則表達式模式中從左以後所遇到的內容存儲。存儲子匹配的緩衝區編號從1開始,
連續編號直至最大99個表達式。

每一個緩衝區均可以使用\n訪問,其中n爲一個即是特定緩衝區的一位或兩位十進制數。

例如:"\1""\2""\3"等形式的引用,在正則表達式模式中使用時還須要在前面加上一個反斜線,
將反斜線再次轉義,
例如:"\\1""\\2""\\3"等形式的引用。

以下所示:

'/^\d{4}\W\d{2}\W\d{2$}/'    // 這是一個匹配日期的格式,如2008-08/08或者2008/08-08 等
'/^\d{4}(\W)\d{2}\\1\d{2$}/'    // 這是一個匹配日期的格式,如2008-08-08或者2008/08/08 等

當你要使用模式單元而又不想存儲匹配結果時,可使用非捕獲元字符"?:""?=""?!"來忽略對相關匹配的保存。

注意

在一些正則表達式中,使用非存儲模式單元是必要的,能夠改變其後向引用的順序。

`/(Windows)(Linux)\\2OS/`          --- 使用"\2"再次引用第二個緩衝區中的字符串"Linux"
`/(?:Windows)(Linux)\\1OS/`        --- 使用"?:"忽略了第一個子表達式的存儲,因此"\1"引用的就是"Linux"

7. 模式匹配的優先級

在使用正則表達式時,須要注意匹配的書序。一般相同優先級從左到右進行運算,不一樣優先級的運算先高後低。

** 順 序 **    ** 元 字 符 **               ** 描 述 **

1                \                         轉義符號

2                ()、(?:)、(?=)、[]        模式單元和原子表

3               *、+、?、{n}、{n,}、{n,m}  重複匹配

4               ^、$、\b、\B、\A、\Z        邊界限制

5               |                           模式選擇

模式修正符

模式修正符在正則表達式定界符以外使用(最後一個斜線"/"以後)。

模式修正符能夠調整正則表達式的解釋,擴展了正則表達式在匹配、替換等操做時的某些功能;
並且模式修正符能夠組合使用,更加強了正則表達式處理能力

模式修正符能夠單個使用,也能夠多個組合使用

模式修正符

**模式修正符**                        **功能描述**

i                                 在和模式進行匹配時不區分大小寫

m                                 將字符串視爲多行。默認開的正則開始`"^"`和`"$"`將目標字符串做爲單一的一"行"
                                  字符(甚至其中包含有換行符也是如此)。若是在修飾符中加上`"m"`,那麼開始和結束將會指字符串的每一行,每一行的開頭就是`"^"`,結尾就是`"$"`。

s                                 若是設定了次修正符,則模式中的圓點元字符`"."`匹配全部的字符,包括換行符。
                                  即將字符串視爲單行,換行符做爲普通字符看待。

x                                 模式中的空白忽略不計,除非它已經被轉義。

e                                 **只有在`preg_replace()`函數中,在替換字符串對逆向引用作正常的替換,將其做爲
                                  PHP代碼求值,而且其結果來替換所搜索的字符串**

U                                 本修正符反轉了匹配數量的值使其使其不是默認的重複,
                                  而變成在後面跟上`"?"`才變得重複。**__這和Perl語言不兼容__**。也能夠經過在模式中設定(U)修正符或者數量符以後跟一個問號`"?"`(例如.*?)來使用此選項。

D                                 模式中的美圓元字符僅匹配目標字符串的結尾。沒有此選項時,若是最後
                                  一個字符是換行符,則美圓符號也會匹配次字符以前的內容。若是設定了m修正符,則忽略此選項

注意:

  1. 模式 "/Web Server/ix"能夠用來匹配字符串"webserver",忽略大小寫和空白。
  2. 貪婪匹配,在匹配成功的前提下,儘量多的去匹配*,+,{n,},.*是貪婪的,例如:/a.*e/去匹配字符串"abcd fsdfsdfsesfdfsdfsesdfedfsdfses",因爲'.*'是貪婪的匹配,會從這個字符串中匹配出"abcd fsdfsdfsesfdfsdfsesdfedfsdfse",從第一個a開始,知道最後一個字母e結束,都屬於'.*'內容。若是想取消這種貪婪模式,可使用模式修正符"U"或者在模式中使用.*?,在.*後面加個?。爲了兼容Perl正則函數,可能會沒有模式修正符"U",咱們建議使用在".*"後加"?"來實現懶惰模式(即非貪婪模式)若是"U""?"同時使用,像這樣"/a.*?e/U",則匹配"abcdfsdfsdfsesfdfsdfsesdfedfsdfse",至關於又啓用了貪婪模式。
  3. 惰性模式,在匹配成功的前提下,儘量少的去匹配(注意點同上)
  4. 模式"/^is/m"能夠匹配字符串"this\nis\nais\ntest"中的'is',由於使用模式修正符"m"將字符串視爲多行,第二行出現的"is"匹配成功。默認的正則開始^和結束'$'將目標字符串做爲單一的一「行」(甚至包含換行符也是如此)。

Note:

看到這裏是否是懵逼了?沒錯,我也懵逼。
不過沒有關係,秉承Learning by doing的準則,
在接下來的教程中,我會一一演示。

更多文章請訪問個人博客:Noapes

實例

/**
 *  無亂碼截取中文字符
 * @param $str
 * @param int $start
 * @param $length
 * @param string $charset
 * @param bool|true $suffix
 * @return string|void
 */

function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true)
{
    if(function_exists("mb_substr"))
        return mb_substr($str, $start, $length, $charset);
    elseif(function_exists('iconv_substr')) {
        return iconv_substr($str,$start,$length,$charset);
    }
    $re['utf-8']   = "/[x01-x7f]|[xc2-xdf][x80-xbf]|[xe0-xef][x80-xbf]{2}|[xf0-xff][x80-xbf]{3}/";
    $re['gb2312'] = "/[x01-x7f]|[xb0-xf7][xa0-xfe]/";
    $re['gbk']    = "/[x01-x7f]|[x81-xfe][x40-xfe]/";
    $re['big5']   = "/[x01-x7f]|[x81-xfe]([x40-x7e]|xa1-xfe])/";
    preg_match_all($re[$charset], $str, $match);
    $slice = join("",array_slice($match[0], $start, $length));
    if($suffix) return $slice;
    return $slice;
}

echo msubstr('哈哈哈你好啊啊',2,3,'gb2312');

  1. sdf
相關文章
相關標籤/搜索