与Perl兼容的正则表达式函数

正则表达式不能独立使用,它只是一种用来定义字符串的规则模式,必须在相应的正则表达式函数中应用,才能实现对字符串的匹配、查找、替换及分割等操作。前面也介绍过在PHP中有两套正则表达式函数库,而使用与Perl兼容的正则表达式函数库的执行效率要略占优势,所以在本书中主要介绍以“preg_”开头的正则表达式函数。

另外,在处理大量信息时,正则表达式函数会使速度大幅减慢,应当只在需要使用正则表达式解析比较复杂的字符串时才使用这些函数。如果要解析简单的表达式,还可以采用很多可以显著加快处理过程的预定义函数。下面将详细地对比介绍。

一、字符串的匹配与查找

①函数preg_match()

该函数在前面也介绍过一些,通常用于表单验证。可以按指定的正则表达式模式,对字符串进行搜索和匹配一次。该函数的语法格式如下所示:

int preg_match(string pattern,string subject[,array matches]) //正则表达式的匹配函数

该函数有两个必选参数,第一个参数pattern需要提供用户按正则表达式语法编写的模式,第二个参数subject需要一个字符串。该函数的作用就是在第二个字符串参数中,搜索与第一个参数给出的正则表达式匹配的内容。如果提供了第三个可选的数组参数matches,则可以用于保存与第一个参数中的子模式的各个部分的匹配结果。正则表达式中的子模式是括号“()”阔起的模式单元,其中数组中的第一个元素matches[0]保存了与正则表达式小括号内子表达式相匹配的内容。例如,matches[1]保存了与正则表达式中第一个小括号内匹配的内容,matches[2]保存了与正则表达式中第二个小括号内匹配的内容,以此类推。该函数只做一次匹配,最终返回0或1的匹配结果数。该函数的使用如下代码所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<?php

//一个用于匹配URL的正则表达式

$pattern='/(https?|ftps?):\/\/(www)\.([^\.\/]+)\.(com|net|org)(\/[\w-\.\/\?\$\&\=]*)?/i';

//被搜索字符串

$subject="网址为http://www.lampbrother.net/index.php的位置是LAMP兄弟连";

//使用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"搜索失败";

}

?>

该程序的输出结果为:

搜索到的URL为:http://www.lampbrother.net/index.php

URL中的协议为:http

URL中的主机为:www

URL中的域名为:lampbrother

URL中的顶域为:net

URL中的文件为:/index.php

②函数preg_match_all()

与函数与preg_match()函数类似,不同的是函数preg_match()在第一次匹配之后就会停止搜索。而函数preg_match_all()则会一直搜索到指定字符串的结尾,可以获取到所有匹配到的结果。该函数的语法格式如下所示:

int preg_match_all(string pattern,string subject,array matches[,int flags])

该函数将把所有可能的匹配结果放入第三个参数的数组中,并返回整个模式匹配的次数,如果出错则返回False。如果使用了第四个参数,会根据它指定的顺序将每次出现的匹配结果保存到第三个参数的数组中。第四个参数flags有以下两个预定义的值。

PREG_PATTERN_ORDER:它是preg_match_all()函数的默认值,对结果排序使$matches[0]为全部模式匹配的数组,$matches[1]为第一个括号中的子模式所匹配的字符串组成的数组,依此类推。

PREG_SET_ORDER:对结果排序使$matches[0]为第一组匹配项的数组,$matches[1]为第二组匹配项的数组,依此类推。

将上例中的代码重新改写一下,使用preg_match_all()函数去搜索指定字符串中所有URL,并将获取每个URL的整体内容及各自的组成部分。该函数的使用如下代码所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<?php

//声明一个可以匹配URL的正则表达式

$pattern='/(https?|ftps?):\/\/(www|bbs)\.([^\.\/]+)\.(com|net|org)(\/[\w-\.\/\?\$\&\=]*)?/i';

//声明一个包含多个URL连接地址的多行文字

$subject= "网址为http://bbs.lampbrother.net/index.php的位置是LAMP兄弟连,

网址为http://www.baidu.com/index.php的位置是百度,

网址为http://www.google.com/index.php的位置是谷歌";

$i=1;//定义一个计数器,用来统计搜索到的结果数

//搜索全部的结果

if(preg_match_all($pattern,$subject,$matches,PREG_SET_ORDER)){

//循环遍历二维数组$matches

foreach($matchesas$urls){

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"搜索失败!";

}

?>

该程序的输出结果为:

搜索到第1个URL为:http://bbs.lampbrother.net/index.php

第1个URL中的协议为:http

第1个URL中的主机为:bbs

第1个URL中的域名为:lampbrother

第1个URL中的顶域为:net

第1个URL中的文件为:/index.php

搜索到第2个URL为:http://www.baidu.com/index.php

第2个URL中的协议为:http

第2个URL中的主机为:www

第2个URL中的域名为:baidu

第2个URL中的顶域为:com

第2个URL中的文件为:/index.php

搜索到第3个URL为:http://www.google.com/index.php

第3个URL中的协议为:http

第3个URL中的主机为:www

第3个URL中的域名为:google

第3个URL中的顶域为:com

第3个URL中的文件为:/index.php

③函数preg_grep()

该函数与前面两个函数不同的是匹配数组中的元素,返回与正则表达式匹配的数组单元,该函数的语法格式如下所示:

array preg_grep (string pattern,array input) //匹配数组中的单元

该函数返回一个数组,其中包括了第二个参数input数组中与给定的第一个参数pattern模式相匹配的单元。对于输入数组input中的每个元素,只进行一次匹配。该函数的使用代码如下所示:

1

2

3

4

5

6

7

8

9

10

<?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()

如果只是查找一个字符串中是否包含某个字符串,建议使用strstr()或strpos()函数,如果只是简单地从一个字符串中取出一段子字符串,建议使用substr()函数。虽然PHP提供的字符串处理函数不能完成复杂的字符串匹配,但处理一些简单的字符串匹配,执行效率则比使用正则表达式稍高一些。

函数strstr()搜索一个字符串在另一个字符串中的第一次出现,该函数返回字符串的其余部分(从匹配点)。如果未找到所搜索的字符串,则返回false。该函数对大小写敏感,如需进行大小写不敏感的搜索,可以使用个stristr()函数。该函数有两个参数,第一个参数提供被搜索的字符串。第二个参数为所搜索的字符串,如果该参数是数字,则搜索匹配数字ASCII值的字符。该函数的代码如下所示:

1

2

3

4

5

<?php

echostrstr("this is a test!","test");//输出test!

echostrstr("this is a test!",115);//搜索"s"的ASCII值所代表的字符输出s is a test!

?>

函数strpos()返回字符串在另一个字符串中第一次出现的位置,如果没有找到该字符串,则返回false。函数strrpos()和函数strpos()相似,用来查找字符串在另一个字符串中最后一次出现的位置。这两个函数都对大小写敏感,如需进行对大小写不敏感的搜索,可以使用stripos()和strripos()函数。函数substr()则可以返回字符串的一部分。这几个函数的应用都比较容易,在下面的例子中将结合这几个函数获取URL中的文件名称。代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<?php

//用于获取URL中的文件名部分

functiongetFileName($url){

//获取URL字符串中最后一个“/”出现的位置,再加1则为文件名开始的位置

$location=strrpos($url,"/")+1;

//获取在URL中从$location位置取到结尾的子字符串

$fileName=substr($url,$location);

//返回获取道德文件名称

return$fileName;

}

//获取网页文件名index.php

echogetFileName("http://bbs.lampbrother.net/index.php");

//获取网页中图片名logo.gif

echogetFileName("http://bbs.lampbrother.com/images/Sharp/logo.gif");

//获取本地中的文件名php.ini

echogetFileName("file:///c:/WINDOWS/php.ini");

?>

二、字符串的替换

字符串的替换也是字符串操作中非常重要的内容之一。对于一些比较复杂的字符串替换操作,可以通过正则表达式的替换函数preg_replace()来完成。而对字符串做简单的替换处理,建议使用str_replace()函数,这也是从执行效率方面考虑的。

①函数preg_replace()

该函数可以执行正则表达式的搜索与替换,是一个最强大的字符串替换处理函数。该函数的语法格式如下所示:

mixed preg_replace(mixed pattern,mixed preplacement,mixed subject [,int limit])

该函数会在三个参数subject中搜索第一个参数pattern模式的匹配项,并替换为第二个参数replacement。如果制订了第四个可选参数limit,则仅替换limit个匹配,如果省略limit或者其值为-1,则所有的匹配项都会被替换。该函数的使用代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

<?php

//可以匹配所有HTML标记的开始和结束的正则表达式

$pattern="/<[\/\!]*?[^<>]*?>/is";

//声明一个带有多个HTML标记的文本

$text="这个文本中有<b>粗体</b>和<u>带有下划线</u>以及<i>斜体</i>

还有<font color='red'size='7'>带有颜色和字体大小</font>的标记";

//将所有HTML标记替换为空,即删除所有HTML标记

echopreg_replace($pattern,"",$text);

echopreg_replace($pattern,"",$text,2);

?>

上例是preg_replace()函数最简单的用法,只是将文本$text中根据$pattern模式搜索到的HTML标记全部替换为空,即删除HTML标记。也可以通过第四个参数传入一个整数,用来指定替换的次数。

使用preg_replace函数时,最常见的形式就是可以包含反向引用,即使用\n的形式依次引用正则表达式中的模式单元,如果在双引号中带有“\”则是转义符号,所以双引号中应该去掉“\”转义功能,所以使用“\\n”。每个此种引用将被替换为与第n个被捕获的括号内的子模式所匹配的文本,n可以从0到99。其中\0指的是被整个模式所匹配的文本,对左圆括号从左到右计数(从1开始)以取得子模式的数目。对替换模式在一个逆向引用后面紧接着一个数字时(即紧接在一个匹配的模式后面的数字),不能使用熟悉的\1符号来表示逆向引用。举例说明,\11,将会使preg_replace()搞不清楚是想要一个\1的逆向引用后面跟着一个数字1还是一个\11的逆向引用。本例中的解决方法是使用\${1}1,这会形成一个隔离的$1逆向引用,而使另一个1只是单纯的文字。这种形式的使用代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

<?php

//日期格式的正则表达式

$pattern="/(\d{2})\/(\d{2})\/(\d){4}/";

//带有两个日期格式的字符串

$text="今年国庆节放假日期为10/01/2012到10/07/2012共七天。";

//将日期替换为以“-”分隔的格式

echopreg_replace($pattern,"\\3-\\1-\\2",$text);

//将“\\1”改为“\$(1)”的形式

echopreg_replace($pattern,"\${3}-\${1}-\${2}",$text);

?>

该程序的输出结果为:

今年国庆节放假日期为2012-10-01到2012-10-07共7天。

今年国庆节放假日期为2012-10-01到2012-10-07共7天。

在使用preg_replace()函数时,有一个专门为它提供的模式修正符“e”,也只有preg_replace()函数使用此修正符。如果设定了此修正符,函数preg_replace()在替换字符串中对逆向引用做正常的替换,将其作为PHP代码求值,并用结果来替换所搜索的字符串。要确保第二个参数构成一个合法的PHP代码字符串,否则PHP会在报告中包含preg_replace()的行中出现语法解析错误。使用代码如下所示:

1

2

3

4

5

6

7

8

9

10

<?php

//可以匹配所有HTML标记的开始和结束的正则表达式

$pattern="/(<\/?)(\w+)([^>]*>)/e";

//声明一个带有多个HTML标记的文本

$text="这个文本中有<b>粗体</b>和<u>带有下划线</u>以及<i>斜体</i>

还有<font color='red'size='7'>带有颜色和字体大小</font>的标记";

//将所有HTML的小写标记替换为大写

echopreg_replace($pattern,"'\\1'.strtoupper('\\2').'\\3'",$text);

?>

在上例中声明正则表达式时,使用了模式修正符“e”。所以函数preg_replace()中第二个参数的字符串“’\\1′.strtoupper(‘\\2′).’\\3′”将作为PHP代码求值,执行了strtoupper函数将模式中的第二个子表达式转换为大写,否则将不会执行此函数。

在使用preg_replace()函数时,其前三个参数均可以使用数组。如果第三个参数是一个数组,则会对他中的每个元素都执行搜索和替换,并返回替换后的一个数组。如果第一个参数和第二个参数都是数组,则preg_replace()函数会依次从中分别取出对应的值来对第三个参数中的文本进行搜索和替换。如果第二个参数中的值比第一个参数中的少,则用空字符串为余下的替换值。如果第一个参数是数组而第二个参数是字符串,则对第一个参数中的每个值都用此字符串作为替换值,反过来则没有意义了。

②函数str_replace()

该函数是PHP系统提供的字符串处理函数,也可以实现字符串的替换工作。虽然没有正则表达式的替换函数功能强大,但一些简单字符串的替换要比使用preg_replace()函数的执行效率稍高。该函数的语法格式如下所示:

mixed str_replace(mixed search,mixed replace,mixed subject [,int&count]) //字符串替换函数

该函数有三个必选参数,还有一个可选参数。第一个参数search为目标对象,第二个参数replace是替换对象,第三个参数subject则是被处理的字符串。该函数在第三个参数的字符串中,以区分大小写的方式搜索第一个参提供的目标对象,并用第二个参数所提供的替换对象替换找到的所有实例。如果没有在第三个参数中搜索到目标对象,则被处理的字符串保持不变。在PHP5.0以后还可以使用第四个可选参数,是一个变量的引用,必须传入一个变量名称,用来保存替换的次数。如果执行以不区分大小写的方式搜索可以使用str_ireplace()函数,与str_replace()函数用法相同,都返回替换后的字符串。代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<?php

//声明包含多个“LAMP”字符串的文本,也包含小写的“lamp”字符串

$str="LAMP是目前最流行的WEB开发平台;<br>

LAMP为B/S架构软件开发的黄金组合;<br>

LAMP每个成员都是开源软件;<br>

lampBrother是LAMP的技术社区。<br>";

//区分大小写的将“LAMP”替换为“Linux+Apache+MySQL+PHP”,并统计替换次数

echostr_replace("LAMP","Linux+Apache+MySQL+PHP",$str,$count);

echo"区分大小写时共替换".$count."次<br>";//替换4次

//不区分大小写的将“LAMP”替换为“Linux+Apache+MySQL+PHP”,并统计替换次数

echostr_ireplace("LAMP","Linux+Apache+MySQL+PHP",$str,$count);

echo"不区分大小写时共替换".$count."次<br>";//替换5次

?>

该程序的输出结果为:

Linux+Apache+MySQL+PHP是目前最流行的WEB开发平台;

Linux+Apache+MySQL+PHP为B/S架构软件开发的黄金组合;

Linux+Apache+MySQL+PHP每个成员都是开源软件;

lampBrother是Linux+Apache+MySQL+PHP的技术社区。

区分大小写时共替换4次

Linux+Apache+MySQL+PHP是目前最流行的WEB开发平台;

Linux+Apache+MySQL+PHP为B/S架构软件开发的黄金组合;

Linux+Apache+MySQL+PHP每个成员都是开源软件;

Linux+Apache+MySQL+PHPBrother是Linux+Apache+MySQL+PHP的技术社区。

不区分大小写时共替换5次

函数str_replace()的前两个参数不仅可以使用字符串,也可以是数组。当在第一个参数中包含多个目标字符串数组时,该函数可以在第二个参数中使用同一个替换字符串,替换在第三个参数中通过第一个参数搜索到每一个元素。代码如下所示:

1

2

3

4

5

6

7

8

9

10

<?php

//元音字符数组

$vowels=array("a","e","i","o","u","A","E","I","O","U");

//将第三个参数中的字符串,搜索到数组中的元素值都被替换为空,区分大小写替换

echostr_replace($vowels,"","HELLO WORLD OF PHP");//输出:HLL WRLD PHP

//元音字符数组

$vowels=array("a","e","i","o","u");

//将第三个参数中的字符串,搜索到的数组中的元素值都被替换为空,不区分大小写替换

echostr_ireplace($vowels,"","HELLO WORLD OF PHP");//输出:HLL WRLD F PHP

?>

如果第一个参数的目标对象和第二个参数的替换对象,都是包含多个元素的数组,通常两个数组中的元素要彼此对应,该函数将使用第二个参数中的元素,替换和它对应的第一个参数中的元素。如果第二个参数中的元素比第一个参数中的元素少,则少的部分使用空替换。代码如下所示。

1

2

3

4

5

6

7

8

9

10

<?php

//元音字符数组

$vowels=array("a","e","i","o","u","A","E","I","O","U");

//将第三个参数中的字符串,搜索到数组中的元素值都被替换为空,区分大小写替换

echostr_replace($vowels,"","HELLO WORLD OF PHP");//输出:HLL WRLD PHP

//元音字符数组

$vowels=array("a","e","i","o","u");

//将第三个参数中的字符串,搜索到的数组中的元素值都被替换为空,不区分大小写替换

echostr_ireplace($vowels,"","HELLO WORLD OF PHP");//输出:HLL WRLD F PHP

?>

三、字符串的分割和连接

在进行字符串分析时,还经常需要对字符串进行分割和连接处理。同样有两种处理函数,复杂的字符串分割,可以使用正则表达式的分割函数preg_split()按模式对字符进行分割。简单的字符串分割处理,就需要使用字符串处理函数explode()进行分割。字符串的连接除了可以使用点“.”运算符外,还可以使用字符串处理函数implode()将数组中所有的字符串元素连接成一个字符串。

①函数preg_split()

该函数使用了Perl兼容的正则表达式语法,可以按正则表达式的方法分割字符串,因此可以使用更广泛的分隔符。该函数的语法格式如下所示:

array preg_split(string pattern,string subject[,int limit[,int flags]]) //使用正则表达式分割字符串

本函数返回一个字符串数组,数组中元素包含通过第二个参数subject中的字符串,经第一个参数的正则表达式pattern,作为匹配的边界所分割的子串。如果指定了第三个可选参数limit,则最多返回limit个子串,而其中最后一个元素包含了subject中剩余的所有部分。如果limit是-1,则意味着没有限制。还可以用来继续指定第四个可选参数flags,其中flags可以是下列标记的任意组合(用按位或运算符|组合)。

PREG_SPLIT_NO_ENMPTY:如果设定了本标记,则preg_split()只返回非空的成分。

PREG_SPLIT_DELIM_CAPTURE:如果设定了本标记,定界符模式中的括号表达式也会被捕捉并返回。

PREG_SPLIT_OFFSET_CAPTURE:如果设定了本标记,对每个出现的匹配结果也同时返回其附属的字符串偏移量。注意这改变了返回的数组的值,使其中的每个单元也是一个数组,其中第一项为匹配字符串,第二项为其在subject中的偏移量。

该函数的使用代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<?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))

*

* */

?>

②函数explode()

如果仅用某个特定的字符串进行分割,建议使用explode()函数,它不用去调用正则表达式引擎,因此速度是最快的。该函数的语法如下所示:

array explode(string separator ,sting sting[,int limit]) //字符串分割函数

该函数有三个参数,第一个参数separator提供了一个分割字符或是字符串,第二个参数string是被分割的字符串。如果提供第三个可选参数limit,则指定最多将字符串分割为多少个子串。该函数返回一个由被分割的子字符串组成的数组。如果separator为空字符串(“”),explode()将返回FALSE。如果separator所包含的值在string中找不到,那么explode()将返回包含string单个元素的数组。该函数的应用代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<?php

$lamp="Linux Apache MySQL PHP";//每个单词之间使用空格分割

$lampbrother=explode(" ",$lamp);//将字符串$lamp使用空格分割,并组成数组返回

echo$lampbrother[2];//输出数组中第三个元素,即$lamp中的第三个子串MySQL

echo$lampbrother[3];//输出数组中第三个元素,即$lamp中的第四个子串PHP

//将Linux中的用户文件的一行提出

$password="redhat:*:500:508::/home/redhat:/bin/bash";

//按‘:’分割7个字符串

list($user,$pass,$uid,$gid, ,$home,$shell) =explode(":",$password);

echo$user;//输出redhat

echo$pass;//输出*

echo$uid;//输出500

echo$gid;//输出508

echo$home;//输出/home/redhat

echo$shell;//输出/bin/bash

//声明字符串$lamp,每个单词之间使用加号“+”分割

$lamp="Linux+Apache+MySQL+PHP";

//使用正数限制子串个数,而最后那个元素将包含$lamp中的剩余部分

print_r(explode('+',$lamp,2));//输出Array([0]=>Linux [1]=>Apache+MySQL+PHP)

//使用负数限制子串,则返回除了最后的限制个元素外的所有元素

print_r(explode('+',$lamp,-1));//输出Array([0]=>Linux [1]=>Apache [2]=>MySQL)

?>

③函数implode()

与分割字符串相对应的是implode()函数,把数组中的所有元素组合为一个字符串。函数join()为该函数的别名,语法格式如下所示:

string implode(string glue,array pieces) //连接数组成为字符串

该函数有两个参数,第一个参数glue提供一个连接字符或字符串,将第二个参数pieces指定一个被连接数组。该函数用于将数组pieces中的每个元素用指定的字符串glue连接起来。该函数的应用代码如下所示:

1

2

3

4

5

6

<?php

$lamp=array("Linux","Apache","MySQL","PHP");

echoimplode("+",$lamp);//使用加号连接后输出Linux+Apache+MySQL+PHP

echojoin("+++",$lamp);//使用加号连接后输出Linux+++Apache+++MySQL+++PHP

?>

>> 本文固定链接: http://php.ncong.com/php_course/php_zend/regex-hanshu.html

>> 转载请注明: 恩聪php 2014年08月02日 于 恩聪PHP学习教程 发表