在PHP中使用与Perl兼容的正则表达式
--------------------------------------------------------------------------------
 
 来源:PHP动力在线 
1 前言
    PHP被大量的应用于Web的后台CGI开发,通常是在用户数据数据之后得出某种结果,但是如果用户输入的数据不正确,就会出现问题,比如说某人的生日是"2月30日"!那应该怎么样来检验暑假是否正确呢? 在PHP中加入了正则表达式的支持,让我们可以十分方便的进行数据匹配。 2 什么是正则表达式:
    简单的说,正则表达式是一种可以用于模式匹配和替换的强大工具。在几乎所有的基于UNIX/LINUX系统的软件工具中找到正则表达式的痕迹,例如:Perl或PHP脚本语言。此外,JavaScript这种客户端的脚本语言也提供了对正则表达式的支持,现在正则表达式已经成为了一个通用的概念和工具,被各类技术人员所广泛使用。 
    在某个Linux网站上面有这样的话:"如果你问一下Linux爱好者最喜欢什么,他可能会回答正则表达式;如果你问他最害怕什么,除了繁琐的安装配置外他肯定会说正则表达式。" 
    正如上面说的,正则表达式看起来非常复杂,让人害怕,大多数的PHP初学者都会跳过这里,继续下面的学习,但是PHP中的正则表达式有着可以利用模式匹配找到符合条件的字符串、判断字符串是否合乎条件或者用指定的字符串来替代符合条件的字符串等强大的功能,不学实在太可惜了…… 
3 正则表达式的基本语法:
    一个正则表达式,分为三个部分:分隔符,表达式和修饰符。 
    分隔符可以是除了特殊字符以外的任何字符(比如"/ !"等等),常用的分隔符是"/"。表达式由一些特殊字符(特殊字符详见下面)和非特殊的字符串组成,比如"[a-z0-9_-]+@[a-z0-9_-.]+"可以匹配一个简单的电子邮件字符串。修饰符是用来开启或者关闭某种功能/模式。下面就是一个完整的正则表达式的例子: 
/hello.+?hello/is 
    上面的正则表达式"/"就是分隔符,两个"/"之间的就是表达式,第二个"/"后面的字符串"is"就是修饰符。 
    在表达式中如果含有分隔符,那么就需要使用转义符号"\",比如"/hello.+?\/hello/is"。转义符号除了用于分隔符外还可以执行特殊字符,全部由字母构成的特殊字符都需要"\"来转义,比如"\d"代表全体数字。
4 正则表达式的特殊字符:
    正则表达式中的特殊字符分为元字符、定位字符等等。 
    元字符是正则表达式中一类有特殊意义的字符,用来描述其前导字符(即元字符前面的字符)在被匹配的对象中出现的方式。元字符本身是一个个单一的字符,但是不同或者相同的元字符组合起来可以构成大的元字符。 
元字符: 
    大括号:大括号用来精确指定匹配元字符出现的次数,例如"/pre{1,5}/"表示匹配的对象可以是"pre"、"pree"、"preeeee"这样在"pr"后面出现1个到5个"e"的字符串。或者"/pre{,5}/"代表pre出现0此到5次之间。 
    加号:"+"字符用来匹配元字符前的字符出现一次或者多次。例如"/ac+/"表示被匹配的对象可以是"act"、"account"、"acccc"等在"a"后面出现一个或者多个"c"的字符串。"+"相当于"{1,}"。 
    星号:"*"字符用来匹配元字符前的字符出现零次或者多次。例如"/ac*/"表示被匹配的对象可以是"app"、"acp"、"accp"等在"a"后面出现零个或者多个"c"的字符串。"*"相当于"{0,}"。 
    问号:"?"字符用来匹配元字符前的字符出现零次或者1次。例如"/ac?/"表示匹配的对象可以是"a"、"acp"、"acwp"这样在"a"后面出现零个或者1个"c"的字符串。"?"在正则表达式中还有一个非常重要的作用,即"贪婪模式"。      还有两个很重要的特殊字符就是"[ ]"。他们可以匹配"[]"之中出现过的字符,比如"/[az]/"可以匹配单个字符"a"或者"z";如果把上面的表达式改成这样"/[a-z]/",就可以匹配任何单个小写字母,比如"a"、"b"等等。 
    如果在"[]"中出现了"^",代表本表达式不匹配"[]"内出现的字符,比如"/[^a-z]/"不匹配任何小写字母!并且正则表达式给出了几种"[]"的默认值: 
[:alpha:]:匹配任何字母 
[:alnum:]:匹配任何字母和数字 
[:digit:]:匹配任何数字 
[:space:]:匹配空格符 
[:upper:]:匹配任何大写字母 
[:lower:]:匹配任何小写字母 
[:punct:]:匹配任何标点符号 
[:xdigit:]:匹配任何16进制数字 另外下面这些特殊字符在转义符号"\"转义后代表的含义如下: 
s:匹配单个的空格符 
S:用于匹配除单个空格符之外的所有字符。 
d:用于匹配从0到9的数字,相当于"/[0-9]/"。 
w:用于匹配字母,数字或下划线字符,相当于"/[a-zA-Z0-9_]/"。 
W:用于匹配所有与w不匹配的字符,相当于"/[^a-zA-Z0-9_]/"。 
D:用于匹配任何非10进制的数字字符。 
.:用于匹配除换行符之外的所有字符,如果经过修饰符"s"的修饰,"."可以代表任意字符。     利用上面的特殊字符可以很方便的表达一些比较繁琐的模式匹配。例如"/\d0000/"利用上面的正则表达式可以匹配万以上,十万一下的整数字符串。 定位字符: 
    定位字符是正则表达式中又一类非常重要的字符,它的主要作用是用于对字符在匹配对象中的位置进行描述。 
^:表示匹配的模式出现在匹配对象的开头(和在"[]"里面不同) 
$:表示匹配的模式出现在匹配对象的末尾 
空格:表示匹配的模式出现在开始和结尾的两个边界之一 
"/^he/":可以匹配以"he"字符开头的字符串,比如hello、height等等; 
"/he$/":可以匹配以"he"字符结尾的字符串即she等; 
"/ he/":空格开头,和^的作用一样,匹配以he开头的字符串; 
"/he /":空格结束,和$的作用一样,匹配以he结尾的字符串; 
"/^he$/":表示只和字符串"he"匹配。 括号: 
    正则表达式除了可以用户匹配,还可以用括号"()"来记录需要的信息,储存起来,给后面的表达式读取。比如: 
/^([a-zA-Z0-9_-]+)@([a-zA-Z0-9_-]+)(.[a-zA-Z0-9_-])$/ 
就是记录邮件地址的用户名,和邮件地址的服务器地址(形式为[email protected]之类的),在后面如果想要读取记录下来的字符串,只是需要用"转义符+记录的次序"来读取。比如"\1"就相当于第一个"[a-zA-Z0-9_-]+","\2"相当于第二个([a-zA-Z0-9_-]+),"\3"就是第三个(.[a-zA-Z0-9_-])。但是在PHP中,"\"是一个特殊的字符,需要转义,所以""到了PHP的表达式中就应该写成"\\1"。 
其他特殊符号: 
"|":或符号"|"和PHP里面的或一样,不过是一个"|",而不是PHP的两个"||"!意思就是可以是某个字符或者另一个字符串,比如"/abcd|dcba/"可能匹配"abcd"或者"dcba"。
5 贪婪模式:
前面在元字符中提到过"?"还有一个重要的作用,即"贪婪模式",什么是"贪婪模式"呢? 
比如我们要匹配以字母"a"开头字母"b"结尾的字符串,但是需要匹配的字符串在"a"后面含有很多个"b",比如"a bbbbbbbbbbbbbbbbb",那正则表达式是会匹配第一个"b"还是最后一个"b"呢?如果你使用了贪婪模式,那么会匹配到最后一个"b",反之只是匹配到第一个"b"。 
    使用贪婪模式的表达式如下: 
/a.+?b/ 
/a.+b/U 
不使用贪婪模式的如下: 
/a.+b/ 
上面使用了一个修饰符U,详见下面的部分。
6 修饰符:
    在正则表达式里面的修饰符可以改变正则的很多特性,使得正则表达式更加适合你的需要(注意:修饰符对于大小写是敏感的,这意味着"e"并不等于"E")。正则表达式里面的修饰符如下: 
i :如果在修饰符中加上"i",则正则将会取消大小写敏感性,即"a"和"A" 是一样的。 
m:默认的正则开始"^"和结束"$"只是对于正则字符串如果在修饰符中加上"m",那么开始和结束将会指字符串的每一行:每一行的开头就是"^",结尾就是"$"。 
s:如果在修饰符中加入"s",那么默认的"."代表除了换行符以外的任何字符将会变成任意字符,也就是包括换行符! 
x:如果加上该修饰符,表达式中的空白字符将会被忽略,除非它已经被转义。 
e:本修饰符仅仅对于replacement有用,代表在replacement中作为PHP代码。 
A:如果使用这个修饰符,那么表达式必须是匹配的字符串中的开头部分。比如说"/a/A"匹配"abcd"。 
E:与"m"相反,如果使用这个修饰符,那么"$"将匹配绝对字符串的结尾,而不是换行符前面,默认就打开了这个模式。 
U:和问号的作用差不多,用于设置"贪婪模式"。

解决方案 »

  1.   

    7 PCRE相关的正则表达式函数:
    PHP的Perl兼容正则表达式提供的多个函数,分为模式匹配,替换和匹配数目等等: 
    1、preg_match : 
    函数格式:int preg_match(string pattern, string subject, array [matches]); 
    这个函数会在string中使用pattern表达式来匹配,如果给定了[regs],就会将string记录到[regs][0]中,[regs][1]代表使用括号"()"记录下来的第一个字符串,[regs][2]代表记录下来的第二个字符串,以此类推。preg如果在string中找到了匹配的pattern,就会返回"true",否则返回"false"。 2、preg_replace : 
    函数格式:mixed preg_replace(mixed pattern, mixed replacement, mixed subject); 
    这个函数会使用将string中符合表达式pattern的字符串全部替换为表达式replacement。如果replacement中需要包含pattern的部分字符,则可以使用"()"来记录,在replacement中只是需要用"\1"来读取。3、preg_split : 
    函数格式:array preg_split(string pattern, string subject, int [limit]); 
    这个函数和函数split一样,区别仅在与split可以使用简单正则表达式来分割匹配的字符串,而preg_split使用完全的Perl兼容正则表达式。第三个参数limit代表允许返回多少个符合条件的值。 4、preg_grep : 
    函数格式:array preg_grep(string patern , array input); 
    这个函数和preg_match功能基本上,不过preg_grep可以将给定的数组input中的所有元素匹配,返回一个新的数组。 下面举一个例子,比如我们要检查Email地址的格式是否正确:
    <?php 
    function emailIsRight($email) { 
    if (preg_match("^[_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3}$",$email)) { 
    return 1; 

    return 0; 

    if(emailIsRight('[email protected]')) echo '正确<br>'; 
    if(!emailIsRight('y10k@fffff')) echo '不正确<br>'; 
    ?> 
    上面的程序会输出"正确<br>不正确"。8.PHP中的Perl兼容正则表达式和Perl/Ereg正则表达式的区别: 
    虽然叫做“Perl兼容正则表达式”,但是和Perl的正则表达式相比,PHP的还是由一些不同,比如修饰符“G”在Perl里面代表全部匹配,但是在PHP中没有加入对这个修饰符的支持。 
    还有就是和ereg系列函数的区别,ereg也是PHP中提供的正则表达式函数,不过和preg相比,要弱上很多。 1、ereg里面是不需要也不能使用分隔符和修饰符的,所以ereg的功能比preg要弱上不少。 
    2、关于".":点在正则里面一般是除了换行符以外的全部字符,但是在ereg里面的"."是任意字符,即包括换行符!如果在preg里面希望"."能够包括换行符,可以在修饰符中加上"s"。 
    3、ereg默认使用贪婪模式,并且不能修改,这个给很多替换和匹配带来麻烦。 
    4、速度:这个或许是很多人关心的问题,会不会preg功能强大是以速度来换取的?不用担心,preg的速度要远远比ereg快,笔者做了一个程序测试: time test: PHP代码: <?php
    echo "Preg_replace used time:"; 
    $start = time(); 
    for($i=1;$i<=100000;$i++) { 
    $str = "ssssssssssssssssssssssssssss"; 
    preg_replace("/s/","",$str); 

    $ended = time()-$start; 
    echo $ended; 
    echo " 
    ereg_replace used time:"; 
    $start = time(); 
    for($i=1;$i<=100000;$i++) { 
    $str = "ssssssssssssssssssssssssssss"; 
    ereg_replace("s","",$str); 

    $ended = time()-$start; 
    echo $ended; 
    echo " 
    str_replace used time:"; 
    $start = time(); 
    for($i=1;$i<=100000;$i++) { 
    $str = "sssssssssssssssssssssssssssss"; 
    str_replace("s","",$str); 

    $ended = time()-$start; 
    echo $ended; 
    ?> 
    结果: 
    Preg_replace used time:5 
    ereg_replace used time:15 
    str_replace used time:2 str_replace因为不需要匹配所以速度非常快,而preg_replace的速度比ereg_replace要快上不少。 
    9.关于PHP3.0对于preg的支持:
        在PHP 4.0中默认加入了preg支持,但是在3.0中确没有。如果在3.0中希望使用preg函数,必须加载php3_pcre.dll文件,只要在php.ini的extension部分设置加入"extension = php3_pcre.dll"然后从新启动PHP就可以了! 
        其实正则表达式还常用于UbbCode的实现,很多PHP论坛都使用了这个方法(比如zForum zphp.com或者vB vbullent.com),但是具体的代码比较长。
      

  2.   

    多谢kingerq(多菜鸟)兄的辛劳和坦诚,小弟发“请资深人士开讲座”系列帖子的本意是想和大家一起探讨一些PHP中常见的问题和难点,这其中小弟也了解一些理论和实作技巧,但对于深层次的东西,我还想学习得更多,我想这也是大家共同的心愿。
    PHP手册是个好东西,但是相比起来实作的经验和相互的交流更重要,上CSDN的朋友未必水平都一样,熟悉和精通的方向也不一样,开一系列集思广益的帖子,高手们多上一些实作的程序和个人的心得、体会、技巧,我认为这对大家都是有帮助的。
      

  3.   

    正则表达式是字符串处理的核心,而PHP也是以字符串为核心的,以前常听说perl字符串处理能力多么强,现在的PHP继承了它的很多优点,现在的perl在web这一块已经没有什么生存余地了;-)
    其实正则表达式也没什么好谈的,本人常用的就这两个函数,preg_match和preg_replace,正则的关键就是pattens的书写了,首先要学会正则的一些基本语法,然后自己动手写几个简单的试试看,剩下的可能就是一个长期积累的过程了,多看高手们的例子,试着自己写一些,比较极端的学习方法是学习Linux下面的sed,很快你就会成为一个正则高手
      

  4.   

    贴上唠叨老大的解决小弟问题的一个正则表达式例子,嘿嘿,小弟现学现卖。问题如下:将一段含有回车和换行符的字符串$text分段显示,并在每一段的开头加上两个空格。解决方案:
    $text = "&nbsp;&nbsp".nl2br(preg_replace("/([\r\n]+)/","\\1&nbsp;&nbsp;",$text));
    echo $text;请大家注意    "/([\r\n]+)/","\\1&nbsp;&nbsp;"   这一段
      

  5.   

    介绍一个函数,可能很多人都知道了吧?呵呵
    preg_split()正则分割字符串,兼容perl
    <?php
    $word="Darling,I love you.   520";
    $array=preg_split("/[^a-z0-9]+/i",$word);
    print_r($array)
    ?>
      

  6.   

    了解了基本的东西,就必须多用、多写、多看后才有可能真正学到手。你楼上说得那段,在 正则 中叫做'向后引用'。下载《script56.chm》看看,讲得很入门。
      

  7.   

    恩,前帖已经把正则式讲的比较清楚了。编译原理没学好,呵呵,就知道有个正则文范,不知其所以然了,下面有一强人讲的我一塌糊涂,给你参考http://www.cnblogs.com/Hush/archive/2004/09/06/40361.aspx转:昨天和Sumtec谈到自动机和语法分析,一下子脑子有点混乱,把一些概念搞混了,看了半天清华的编译书也没有整明白...今天早上起来看了《离散数学及其应用》里的自动机一部分,才厘清了头绪。还是外国人的书讲得清楚一点。  昨天主要是把NFA和语法分析中的LL(1) LR(1)搞混了。事实上LL(1)分析也好LR(1)分析也好,使用的是一个基于下推自动机的计算模型,而不是有限自动机。下推自动机的计算能力要比有限自动机强。 其次就是NFA和DFA的计算能力确实是等价的,也就是对于任意一个NFA都可以找到一个与之等价的DFA(可以使用子集法来构造这样一个DFA)。为了说明有限自动机与LL(1) LR(1)等分析法的关系,先概述一下文法的分类 文法分为四类:(1)短语文法(0型文法)(2)上下文相关文法(1型文法)(3)上下文无关文法(2型文法)(4)正规(则)文法(3型文法) 上面四种文法有包含的关系,1型文法是0型文法的一个子集,2型文法是1型文法的一个子集,,3型文法是2型文法的一个子集。 我们主要研究2型和3型方法。 3型文法(正则文法)与正则表达式(Regular Expression)是等价的,任意一个正则文法总是可以转化成一个等价的正则表达式。同时正则表达式与有限自动机是等价的。一个能由有限自动机识别的语言,必然可以用正则表达式来表示,而一个用正则表达式表示的语言一定可以用一个有限自动机来识别。 但是正则文法不足以描述程序设计语言(比如,不能用正则表达式定义带有括号的数学表达式),现在流行的程序设计语言如C#, java等都是用2型文法,也就是上下文无关文法来定义的。因此有限自动机没有能力来识别程序设计语言(最后我会举个例子)。因此提出来下推自动机的模型。下推自动机具有限自动机的所有部件,如状态、状态转移表等,同时它比有限自动机多一个堆栈,常称为计算栈。下推自动机可以根据情况将终结符或者非终结符压入栈,或者弹出栈。 而LL(1),LR(1)等分析法就是用来分析上下文无关文法的,基于下推自动机的模型。这也是为什么介绍语法分析时,所有的书都会说一个基于LR(1)分析法的预测分析器,都会有三部分组成:状态转移表、控制器和计算栈。而所谓的移进与归约就是入栈与出栈的问题。 最后,举一个例子(好象大家都睡着了 -_-b) 先给出一个文法: S->0S1 | 01 其中0、1是终结符。 这样一个文法描述的语言其实就是n个0加上相等数量的n个1,这里n是某个整数。 这个文法是一个上下文无关文法,但不是一个正则文法。所以说我们没办法写出一个正则表达式来描述这样一个语言。等于一个能识别这个文法中的任意句子的NFA,我们总能找到这样一个句子,它不是由该文法所定义的,却能被这个NFA接受。换句话说,任意NFA都不能用来判断某个句子是不是由以上这个文法所定义。说得实际一点,如果要求写一个着色程序,输入的文件是一串0、1组成的序列,要求把其中n个0加n个1的序列用红色着色,而其它用黑色的话,我们就不能光用正则表达式匹配来完成这一任务了。 如果上面这个例子还比较抽象的话,那么对于“带有括号的数学表达式”这样一个语言,也是没有办法用正则表达式来进行匹配的。因为描述“带有括号的数学表达式”的文法,也不是一个正则文法,因为其中带有类似:F->(E)这样的部分。而正则文法要求所有的产生式都必须是A->aB或者A->a这样的形式的(其中A、B是非终结符,a是终结符)
      

  8.   

    http://www.phpe.net/articles/268.shtml
      

  9.   

    网上有本Mastering_Regular_Expressions.pdf,是关于正则表达式的电子书,英文的,有兴趣的可以去down下来看看
      

  10.   

    唠叨老大曾经写过一个JAVASCRIPT正则地练习程序,不知道他可不可以写一个PHP的。
    呵呵,有点无理的要求。
      

  11.   

    其实我的目的很简单,“请资深人士开讲座”系列不外乎就是帮初学者摸清楚一些概念,帮入门不久的朋友提供一些经验和心得,帮PHPer建立一个类似于博客的版面,使大家加深对PHP的理解,仅此而已。
    只要认为是有关于本主题的话题,请各位高人知无不言,言无不尽。
      

  12.   

    前几天找了半天,然后找到这个一个例子
    $text=preg_replace("#<.+?>#is","",$text);
    大家知道,如果用在html线编辑器的话,如果是从word里粘贴过来的,看html语句会有很多html标记,而这些我们不一定需要,所以采用上面的正则表达式就可以把这些清除掉,大家也许可以考虑到这样也可能会把 <br>给清除,那大家在使用上面的正则表达式的时候,可以先把<br>转换一下 ,清除完别的标记后,可以再把<br>给转换会来。