7.PHP正則表達式

正則表達式介紹

正則表達式簡介

正則表達式是用于描述字符排列和匹配模式的一種語法規則。它主要用于字符串的模式分割、匹配、查找及替換操作。到目前為止,我們前面所用過的精確(文本)匹配也是一種正則表達式。

在PHP中,正則表達式一般是由正規字符和一些特殊字符(類似于通配符)聯合構成的一個文本模式的程序性描述。

PHP中,正則表達式的三個作用:
  • 匹配,也常常用于從字符串中析取信息。
  • 用新文本代替匹配文本。
  • 將一個字符串拆分為一組更小的信息塊。

在PHP中有兩套正則表達式函數庫,兩者功能相似,只是執行效率略有差異:

  • 一套是由PCRE(Perl Compatible Regular Expression)庫提供的。使用“preg_”為前綴命名的函數;
  • 一套由POSIX(Portable Operating System Interface of Unix )擴展提供的。使用以“ereg_”為前綴命名的函數;

PCRE來源于Perl語言,而Perl是對字符串操作功能最強大的語言之一,PHP的最初版本就是由Perl開發的產品。
PCRE語法支持更多特性,比POSIX語法更強大。

舉例:想一想這兩個正則表達式做什么用?

/^-?\d+$|^-?0[xX][\da-fA-F]+$/
/^[0-9a-zA-Z_-]+@[0-9a-zA-Z_-]+(\.[0-9a-zA-Z_-]+){0,3}$/
與Perl語言兼容的正則表達式處理函數
函數名 功能描述
preg_match() 進行正則表達式匹配
preg_match_all() 進行全局正則表達式匹配
preg_replace() 執行正則表達式的搜索和替換
preg_split() 用正則表達式分割字符串
preg_grep() 返回與模式匹配的數組單元
preg_replace_callback() 用回調函數執行正則表達式的搜索和替換

正則表達式的語法規則

正則表達式作為一個匹配的模版,是由原子(普通字符,例如字符a到z)、特殊字符(元字符,例如*、+和?等)、以及模式修正符三部分組成的文字模式。
一個最簡單正則表達式至少包含一個原子。
將下面的正則表達式拆分如下:

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

定界符:兩個斜線”/”。
原子用到了< a href = ‘ “ / >等普通字符和\t \r \n等轉義字符
元字符使用了 [] () | . ? * + 等具有特殊含義的字符
用到了模式修正符是在定界符最后一個斜線之后的三個字符: s i m

定界符

在程序語言中,使用與Perl兼容的正則表達式,通常都需要將模式表達式放入定界符之間,如“/”。
作為定界符常使用反斜線“/”,如“/apple/”。用戶只要把需要匹配的模式內容放入定界符之間即可。作為定界的字符也不僅僅局限于“/”。除了字母、數字和斜線“\”以外的任何字符都可以作為定界符,像 ‘#’、’|’、’!’ 等都可以的。

  • /</\w+>/ --使用反斜線作為定界符合法
  • |(\d{3})-\d+|Sm --使用豎線”|”作為定界符合法
  • !^(?i)php[34]! --使用豎線”!”作為定界符合法
  • {^\s+(\s+)?$} --使用豎線”}”作為定界符合法
  • /href=‘(.*)’ --非法定界符,缺少結束定界符
  • 1-\d3-\d3-\d4| --非法定界符,缺少其實定界符
原子

原子是正則表達式的最基本的組成單元,而且在每個模式中最少要少包含一個原子。原子是由所有那些未顯示指定為元字符的打印和非打印字符組成,具體分為5類。

  • 普通字符作為原子: 如 az、AZ、0~9 等
  • 一些特殊字符和轉義后元字符作為原子:
    所有標點符號,但語句特殊意義的符號需要轉義后才可作為原子,如:\” \’ * + ? . 等
  • 一些非打印字符作為原子: 如:\f \n \r \t \v \cx
  • 使用“通用字符類型”作為原子:如:\d \D \w \W \s \S。
  • 自定義原子表([])作為原子:如:’/[apj]sp/’ ’/[^apj]sp/’

正則表達式中常用的非打印字符

原子字符 含義描述
\cx 匹配由x指明的控制字符。如\cM匹配一個Control-M或回車符。x的值必須為AZ或az之一
\f 匹配一個換頁符。等價于 \x0c或\cL
\n 匹配一個換行符。等價于 \x0a或\cJ
\r 匹配一個回車符。等價于 \x0d或\cM
\t 匹配一個制表符。等價于 \x09或\cI
\v 匹配一個垂直制表符。等價于 \x0b或\cK

正則表達式中常用的“通用字符類型”

原子字符 描述含義
\d 匹配任意一個十進制數字,等價于[0-9]
\D 匹配任意一個除十進制數字以外的字符,等價于[^0-9]
\s 匹配任意一個空白符,等價于[\f\n\r\t\v]
\S 匹配除空白符以外任何字符,等價于[^\f\n\r\t\v]
\w 匹配任意一個數字、字母或下畫線,等價于[0-9a-zA-Z_]
\W 匹配一個除數字、字母或下畫線以外的任意一個字符,等價于[^0-9a-zA-Z_]
元字符
Paste_Image.png
  • 字符串邊界限制
    在某些情況下,需要對匹配范圍進行限定,以獲得更準確的匹配結果。^$分別指定字符串的開始和結束。
    例如:
在字符串Tom and Jerry chased each other in the house until tom’s uncel come in中
元字符“^”或“\A” 置于字符串的開始確保模式匹配出現在字符串首端;
/^Tom/
元字符“$”或“\Z” 置于字符串的結束,確保模式匹配出現字符串尾端。
/in$/   
如果不加邊界限制元字符,將獲得更多的匹配結果。
/^Tom$/精確匹配     /Tom/模糊匹配
  • 單詞邊界限制
    在使用各種編輯軟件的查找功能時,可以通過選擇“按單詞查找”獲得更準確的結果。正則表達式中也提供類似的功能。
    例如:
在字符串“This island is a beautiful land”中
元字符`\b`對單詞的邊界進行匹配;
/\bis\b/  匹配單詞“is”,不匹配“This”和“island”。
/\bis      匹配單詞“is”和“island”中的“is”,不匹配“This”
元字符“\B”對除單詞邊界以外的部分進行匹配。
/\Bis\B/  將明確的指示不與單詞的左、右邊界匹配,只匹配單詞的內部。所以在這個例子中沒有結果。
/\Bis      匹配單詞“This”中的“is”
  • 重復匹配
    正則表達式中有一些用于重復匹配某些原子的元字符:?*、+。他們主要的區別是重復匹配的次數不同。
    元字符“?”:表示0次或1次匹配緊接在其前的原子。
    例如:
/colou?r/匹配“colour”或“color”。

元字符“*”:表示0次、1次或多次匹配緊接在其前的原子。
例如:

/zo*/可以匹配z、zoo

元字符“+”:表示1次或多次匹配緊接在其前的原子。
例如:

/go+gle/匹配“gogle”、“google”或“gooogle”等中間含有多個o的字符串。
  • 任何一個字符
    元字符.匹配除換行符外任何一個字符。
    相當于:^\n^\r\n。
    例如:
/pr.y/可以匹配的字符串“prey”、“pray”或“pr%y”等。
通常可以使用“.*”組合來匹配除換行符外的任何字符。在一些書籍中也稱其為“全匹配符” 或 “單含匹配符”。
/^a.*z$/表示可以匹配字母“a”開頭,字母“z”結束的任意不包括換行符的字符串。
/.+/ 也可以完成類似的匹配功能所不同的是其至少匹配一個字符。
/^a.+z$/ 匹配“a%z”不匹配字符串“az”。
  • 重復匹配
    元字符{ }準確地指定原子重復的次數,指定所匹配的原子出現的次數。
    “{m}” 表示其前原子恰好出現m次。
    “{m,n}”表示其前原子至少出現m次,至多出現n次。
    “{m,}” 表示其前原子出現不少于m次。
    例如:
/zo{1,3}m/ 只能匹配字符串“zom”、“zoom”、或“zooom”。
/zo{3}m/   只能匹配字符串“zooom”。
/zo{3,}m/ 可以匹配以  “z” 開頭,“m”結束,中間至少為3個“o”的字符串。 
/bo{0,1}u/ 可以匹配字符串“bought a butter”  中的“bou”和“bu”,等價于bo?u。
  • 原子表 -方括號表達式
    原子表[]中存放一組原子,彼此地位平等,且僅匹配其中的一個原子。如果想匹配一個 ”a” 或 ”e” 使用 [ae]。
例如: Pr[ae]y 匹配 ”Pray” 或者 ”Prey ”。

原子表 [^] 或者稱為排除原子表,匹配除表內原子外的任意一個字符。

例如:/p[^u]/匹配“part”中的“pa”,但無法匹配“computer”中的“pu”因為“u”在匹配中被排除。

原子表[-]用于連接一組按ASCII碼順序排列的原子,簡化書寫。

例如:/x[0123456789]/可以寫成x[0-9],用來匹配一個由 “x” 字母與一個數字組成的字符串。

例如:

/[a-zA-Z]/匹配所有大小寫字母
/^[a-z][0-9]$/匹配比如“z2”、 “t6” 、“g7”
/0[xX][0-9a-fA-F]/匹配一個簡單的十六進制數字,如“0x9”。
/[^0-9a-zA-Z_]/匹配除英文字母、數字和下劃線以外任何一個字符,其等價于\W。 
/0?[ xX][0-9a-fA-F]+/匹配十六進制數字,可以匹配“0x9B3C”或者“X800”等。
/<[A-Za-z][A-Za-z0-9]*>/可以匹配“<P>”、“<hl>”或“<Body>”等HTML標簽,并且不嚴格的控制大小寫。
  • 模式選擇符
    元字符|又稱模式選擇符。在正則表達式中匹配兩個或更多的選擇之一。
    例如:
    在字符串“There are many apples and pears.”中, /apple|pear/在第一次運行時匹配“apple”;
    再次運行時匹配“ pear”。也可以繼續增加選項,如: /apple|pear|banana|lemon/

  • 模式單元
    元字符()將其中的正則表達式變為原子(或稱模式單元)使用。與數學表達式中的括號類似,“()”可以做一個單元被單獨使用。
    例如:

/(Dog)+/匹配的“Dog”、“DogDog”、“DogDogDog”,因為緊接著“+”前的原子是元字符“()”括起來的字符串“Dog”。
/You (very )+ old/匹配“You very old”、“You very very old”
/Hello (world|earth)/匹配“Hello world”、“Hello earth”

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

  • 重新使用的模式單元
    系統自動將模式單元“()”中的匹配依次存儲起來,在需要時可以用\1、\2\3的形式進行引用。當正則表達式包含有相同的模式單元時,這種方法非常便于對其進行管理。注意使用時需要寫成“\1”、“\2”
    例如:
/^\d{2}([\W])\d{2}\\1\d{4}$/
匹配“12-31-2006”、“09/27/1996”、“86 01 4321”等字符串。
但上述正則表達式不匹配“12/34-5678”的格式。
這是因為模式“[\W]”的結果“/”已經被存儲。下個位置“\1”引用時,其匹配模式也是字符“/”。
當不需要存儲匹配結果時使用非存儲模式單元“(?:)” 
例如/(?:a|b|c)(D|E|F)\\1g/ 將匹配“aEEg”。
在一些正則表達式中,使用非存儲模式單元是必要的。
否則,需要改變其后引用的順序。上例還可以寫成/(a|b|c)(C|E|F)\\2g/。
模式修正符
模式修正符

貪婪匹配 匹配結果存在歧義時取長
懶惰匹配 匹配結果存在歧義時取短
U 懶惰匹配
i 忽略英文字母大小寫
x 忽略空白
例如實例

與Perl兼容的正則表達式函數

子符串的匹配與查找函數
  • 函數preg_match() --執行一個正則表達式匹配
    int preg_match(string $pattern, string $subject[,array &$matches])
    搜索subject與pattern給定的正則表達式的一個匹配.
<?php
    //preg_match()函數實例

    //一個用于匹配URL的正則表達式
    $pattern = '/(https?|ftps?):\/\/(www)\.([^\.\/]+)\.(com|net|org)(\/[\w-\.\/\?\%\&\=]*)?/i'; 
    //被搜索字符串
    $subject = "網址為http://www.baidu.com/index.php的位置是百度首頁";    
    //使用preg_match()函數進行匹配
    if(preg_match($pattern, $subject, $matches)) {          
        echo "搜索到的URL為:".$matches[0]."<br>";    //數組中第一個元素保存全部匹配結果
        echo "URL中的協議為:".$matches[1]."<br>";    //數組中第二個元素保存第一個子表達式
        echo "URL中的主機為:".$matches[2]."<br>";    //數組中第三個元素保存第二個子表達式
        echo "URL中的域名為:".$matches[3]."<br>";    //數組中第四個元素保存第三個子表達式
        echo "URL中的頂域為:".$matches[4]."<br>";    //數組中第五個元素保存第四個子表達式
        echo "URL中的文件為:".$matches[5]."<br>";    //數組中第六個元素保存第五個子表達式
    } else {
        echo "搜索失?。?;                             //如果和正則表達式沒有匹配成功則輸出
    }  
?>
  • 函數preg_match_all() --執行全局正則表達式匹配
    int preg_match_all(string $pattern ,string $subject ,array &$matches [, int $flags])
    搜索subject中所有匹配pattern給定正則表達式 的匹配結果并且將它們以flag指定順序輸出到matches中. 參數flags是指定matches的數組格式。
<?php
    //聲明一個可以匹配URL的正則表達式
    $pattern = '/(https?|ftps?):\/\/(www|bbs)\.([^\.\/]+)\.(com|net|org)(\/[\w-\.\/\?\%\&\=]*)?/i';  
    //聲明一個包含多個URL鏈接地址的多行文字
    $subject = "網址為http://www.baidu.com/index.php的位置是百度,
            網址為http://www.google.com/index.php的位置是谷歌。";
    $i = 1;    //定義一個計數器,用來統計搜索到的結果數
    //搜索全部的結果
    if(preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER)) {  
    foreach($matches as $urls) {     //循環遍歷二維數組$matches
            echo "搜索到第".$i."個URL為:".$urls[0]."<br>";   
            echo "第".$i."個URL中的協議為:".$urls[1]."<br>";   
            echo "第".$i."個URL中的主機為:".$urls[2]."<br>";   
            echo "第".$i."個URL中的域名為:".$urls[3]."<br>";  
            echo "第".$i."個URL中的頂域為:".$urls[4]."<br>";   
            echo "第".$i."個URL中的文件為:".$urls[5]."<br>";  
            $i++;  //計數器累加
    }   
    } else {
            echo "搜索失?。?;
    } 
  • 函數preg_grep() --返回匹配模式的數組條目
    array preg_grep ( string $pattern , array $input [, int $flags = 0 ] )
    返回給定數組input中與模式pattern 匹配的元素組成的數組。
<?php
    $array = array("Linux RedHat9.0", "Apache2.2.9", "MySQL5.0.51", "PHP5.2.6", "LAMP", "100");
    
    //返回數組中以字母開始和以數字結束,并且沒有空格的單元,賦給變量$version
    $version = preg_grep("/^[a-zA-Z]+(\d|\.)+$/", $array);  
    
    print_r($version);      
    
    //輸出:Array ( [1] => Apache2.2.9 [2] => MySQL5.0.51 [3] => PHP5.2.6 )
?>
  • 其它子串處理函數:strstr()、strpos()、strrpos()、substr()
<?php
    echo strstr("this is a test!", "test");     //輸出test!
    
    echo strstr("this is a test!", 115);        //搜索 "s" 的ASCII值所代表的字符輸出s is a test!
?>

獲取URL中文件名的部分

<?php
    /**
        用于獲取URL中的文件名部分
        @param  string  $url     任何一個URL格式的字符串
        @return string       URL中的文件名稱部分
    */
    function getFileName($url) {
        //獲取URL字符串中最后一個“/”出現的位置,再加1則為文件名開始的位置
        $location = strrpos($url, "/")+1; 
        //獲取在URL中從$location位置取到結尾的子字符串
        $fileName = substr($url, $location);    
        //返回獲取到的文件名稱
        return $fileName;                   
    }
    //獲取網頁文件名index.php
    echo getFileName("http://bbs.lampbrother.net/index.php");           
    //獲取網頁中圖片名logo.gif  
    echo getFileName("http://bbs.lampbrother.com/images/Sharp/logo.gif"); 
    //獲取本地中的文件名php.ini
    echo getFileName("file:///C:/WINDOWS/php.ini"); 
字符串的替換函數
  • preg_replace —執行一個正則表達式的搜索和替換
mixed preg_replace ( mixed $pattern , mixed $replacement,mixed $subject [,int $limit = -1])

搜索subject中匹配pattern的部分, 以replacement進行替換.

  <?php
    //可以匹配所有HTML標記的開始和結束的正則表達式
    $pattern = "/<[\/\!]*?[^<>]*?>/is";             
    
    //聲明一個帶有多個HTML標記的文本
    $text = "這個文本中有<b>粗體</b>和<u>帶有下畫線</u>以及<i>斜體</i>
             還有<font color='red' size='7'>帶有顏色和字體大小</font>的標記";         
    //將所有HTML標記替換為空,即刪除所有HTML標記
    echo preg_replace($pattern, "", $text);     
    
    //通過第四個參數傳入數字2,替換前兩個HTML標記
    echo preg_replace($pattern, "", $text, 2);  
字符串的分割與連接
  • preg_split — 通過一個正則表達式分隔字符串
array preg_split ( string $pattern , string $subject [, int $limit = -1 [, int $flags = 0 ]] )

通過一個正則表達式$pattern分隔給定字符串$subject。其中$limit是最大替換個數。
flags可以是任何下面標記的組合
PREG_SPLIT_NO_EMPTY:返回分隔后的非空部分
PREG_SPLIT_DELIM_CAPTURE:用于分隔的模式中的括號表達式將被捕獲并返回.
PREG_SPLIT_OFFSET_CAPTURE:返回附加字符串偏移量

<?php
    //按任意數量的空格和逗號分隔字符串,其中包含" ", \r, \t, \n and \f
    $keywords = preg_split ("/[\s,]+/", "hypertext language, programming");
    print_r($keywords);     
    //分割后輸出Array ( [0] => hypertext [1] => language [2] => programming ) 
    
    //將字符串分割成字符
    $chars = preg_split('//', "lamp", -1, PREG_SPLIT_NO_EMPTY);
    print_r($chars);        //分割后輸出Array ( [0] => l [1] => a [2] => m [3] => p ) 
    
    //將字符串分割為匹配項及其偏移量
    $chars = preg_split('/ /','hypertext language programming', -1, 
                PREG_SPLIT_OFFSET_CAPTURE);
    print_r($chars);    
    
    /* 分割后輸出:
        Array ( [0] => Array ( [0] => hypertext [1] => 0 ) 
                   [1] => Array ( [0] => language [1] => 10 ) 
                   [2] => Array ( [0] => programming [1] => 19 ) )     */

Have a try
1.編寫一個有效手機號碼的正則表達式。
2.定義一個有效的時間正則表達式。
3.編寫一個函數,使用正則替換方式能夠實現清除字符串中的所有HTML標簽。
4.將下面文件中的代碼中所有的圖片標簽匹配出來,并以表格方式輸出信息

<div class="clearfix goodsBox">
    <a href="goods.php?id=24">
        ![](images/111.jpg)
    </a>
    <a href="goods.php?id=25">
        ![](images/222.jpg)
    </a>
    <a href="goods.php?id=26">
        ![](images/333.jpg)
    </a>
    <a href="goods.php?id=27">
        ![](images/444.jpg)
    </a>
    <a href="goods.php?id=28">
        ![](images/555.jpg)
    </a>
    <a href="goods.php?id=29">
        ![](images/666.jpg)
    </a>
</div>
image.png
Paste_Image.png

使用php正則對表單數據驗證,提示方式不限。


Paste_Image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容