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阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容