0%

正则表达式,字符串的匹配与查找函数,数组切割

PHP中的正则表达式

在php中,有两套正则表达式函数库,两者功能相似、知识执行效率略有差异:

  • PCRE库提供,使用preg_为前缀命名的函数
  • POSIX扩展提供的。使用以ereg_为前缀命名的函数

PCRE来源于Perl语言,而Perl是对字符串操作功能最强大的语言之一,PHP的最初版本就是由Perl开发的产品。

PCRE语法支持更多特性,比POSIX语法更强大。

与Perl语言兼容的正则表达式处理函数

函数名 功能描述
preg_match() 进行正则表达式匹配
preg_match_all() 进行全局正则表达式匹配
preg_replace() 执行正则表达式的搜索和替换
preg_split() 用正则表达式分割字符串
preg_grep() 返回与模式匹配的数组单元
preg_replace_callback 用回调函数执行正则表达式的搜索和替换

正则表达式语法规则

正则表达式作为一个匹配的模板,是由原子(普通字符,例如字符a到z)、特殊字符(元字符,例如*、+和?等)、以及模式修正符三部分组成的匹配模板。

定界符

在程序语言中,使用与Perl兼容的正则表达式,通常都需要将模式表达式放入定界符之间,如/

作为定界符常使用反斜线/,如/apple/。用户只要把需要匹配的模式内容放入定界符之间即可。作为定界的字符也不仅仅局限于/。除了字母、数字和斜线\以外的任何字符都可以作为定界符,像  #|! 等都可以的。

1
2
3
4
5
6
/<\/\w+>/						--使用反斜线作为定界符合法
|(\d{3})-\d+|Sm --使用竖线”|”作为定界符合法
!^(?i)php[34]! --使用竖线”!”作为定界符合法
{^\s+(\s+)?$} --使用竖线”}”作为定界符合法
/href=‘(.*)’ --非法定界符,缺少结束定界符
1-\d3-\d3-\d4| --非法定界符,缺少定界符

原子

原子是正则表达式的最基本的组成单元,而且在每个模式中最少要少包含一个原子。原子是由所有那些未显示指定为元字符的打印和非打印字符组成,具体分为5类。

  • 普通字符作为原子: 如 a~zA~Z0~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_]

元字符

元字符 含义描述
***** 匹配0次、1次或多次其前的原子
+ 匹配1次或多次其前的原子
匹配0次或1次其前的原子
. 匹配除了换行符外的任意一个字符
** **
{n} 表示其前面的原子恰好出现n次
{n,} 表示其前面的原子出现不小于n次
{n,m} 表示其前面的原子至少出现n次,最多出现m次
^或\A 匹配输入字符串的开始位置(或在多行模式下行的开头,即紧随一个换行符之后)
$或\Z 匹配输入字符串的结束位置(或在多行模式下行的结尾,即紧随一个换行符之前)
\b 匹配单词的边界
\B 匹配除单词边界以外的部分
[] 匹配方括号中指定的任意一个原子
[^] 匹配除方括号中的原子以外的任意一个字符
( ) 匹配其整体为一个原子,即模式单元。可以理解为由多个单个原子组成的大原子

字符串边界限制

  • ^:指定字符串的开始
  • &:指定支付的结束
    1
    2
    3
    4
    5
    6
    $check = '/^apple/';	//^匹配字符串开头为apple
    $check = '/apple$/'; //$匹配字符串结尾为apple
    $check = '/^apple$/'; //完全匹配字符串是apple
    $check = '/apple/'; //模糊匹配字符串含有apple
    preg_match($check, 'apple',$info);
    var_dump($info); //输出array(1) { [0]=> string(5) "apple" }

    单词边界限制

    在使用各种编辑软件的查找功能时,可以通过选择“按单词查找”获得更准确的结果。正则表达式中也提供类似的功能。

例如:在字符串“This island is a beautiful land”中

  • \b:对单词的边界进行匹配

  • \B:对除单词边界以外的部分进行匹配

    1
    2
    3
    4
    5
    6
    $check = '/\bis\b/';	//完全匹配单词is
    $check = '/\bis/'; //匹配单词is开头的
    $check = '/\Bis\B/'; //不与单词的左、右边界匹配,只匹配单词的内部。
    $check = '/\Bis/'; //匹配非is开头的单词
    preg_match_all($check, 'This island is a beautiful land',$info);
    var_dump($info);

    重复匹配

    正则表达式中有一些用于重复匹配某些原子的元字符:“?”、“*”、“+”。他们主要的区别是重复匹配的次数不同。

  • ?:表示0次或1次匹配紧接在其前的原子

  • *:表示0次、1次或多次匹配紧接在其前的原子

  • +:表示1次或多次匹配紧接在其前的原子

    1
    2
    3
    4
    5
    $check = '/Th?is/';		//匹配Tis,This
    $check = '/Th*is/'; //匹配Tis,This,Thhis等
    $check = '/Th+is/'; //匹配This,Thhis等
    preg_match($check, 'This',$info);
    var_dump($info);
  • {}:准确的指定原子重复的次数,指定匹配所匹配的原子出现的次数

    • {m}:表示其前原子恰好出现m次
    • {m,n}:表示其前原子至少出现m次,至多出现n次
    • {m,}:表示其前原子出现不少于m次
      1
      2
      3
      4
      5
      $check = '/This{1}/';		//匹配This
      $check = '/This{1,2}/'; //匹配This或Thiss
      $check = '/This{1,}/'; //匹配This...
      preg_match($check, 'Thissss',$info);
      var_dump($info);

      任何一个字符

  • .:匹配除换行符外任何一个字符

    通常可以使用“.*”组合来匹配除换行符外的任何字符。

1
2
3
4
$check = '/^a.*z$/';		//匹配字母a开头,z结尾的任意不包含换行符的字符串
$check = '/^a.+z$/'; //匹配字母a开头,z结尾的三个字符串,不匹配"az"
preg_match($check, 'aasdasdsaz',$info);
var_dump($info);

方括号表达式

  • []:存放一组原子,彼此地位平等,仅匹配其中的一个原子。例如想匹配一个ae,使用[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]*>/可以匹配“

      ”、“”或“”等HTML标签,并且不严格的控制大小写。

模式选择符

  • |:在正则表达式中匹配两个或更多的选择之一

    • 例如:在字符串There are many apples and pears中, /apple|pear/在第一次运行时匹配apple,再次运行时匹配pear。也可以继续增加选项,如:/apple|pear|banana|lemon/

      模式单元符

  • ():将其中的正则表达式变为原子(或称模式单元)使用,与数学表达式中的括号类似。

    • 例1:/(Dog)+/匹配的DogDogDogDogDogDog,因为紧接着+前的原子是元字符()括起来的字符串Dog
    • 例2:/Hello (world|earth)/匹配Hello worldHello earth

      一个模式单元中的表达式将被优先匹配或运算。

重新使用的模式单元

系统自动将模式单元()中的匹配依次存储起来,在需要时可以用\1\2\3的形式进行引用。当正则表达式包含有相同的模式单元时,这种方法非常便于对其进行管理。注意使用时需要写成\\1\\2

  • 例如:/^\d{2}([\W])\d{2}\\1\d{4}$/匹配12-31-200609/27/199686 01 4321等字符串。但上述正则表达式不匹配12/34-5678的格式。这是因为模式[\W]的结果/已经被存储。下个位置\1引用时,其匹配模式也是字符/

当不需要存储匹配结果时使用非存储模式单元(?:)

  • 例如:/(?:a|b|c)(D|E|F)\\1g/ 将匹配aEEg。在一些正则表达式中,使用非存储模式单元是必要的。否则,需要改变其后引用的顺序。上例还可以写成/(a|b|c)(C|E|F)\\2g/

    模式匹配的优先级

顺序 元字符 描述
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 修正符则忽略此选项

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

字符串的匹配和查找函数

  • preg_match():执行一个正则表达式匹配

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 一个用于匹配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 "搜索失败!"; //如果和正则表达式没有匹配成功则输出
    }

    image

  • preg_match_all():执行全局正则表达式匹配

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //声明一个可以匹配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)) {
    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 "搜索失败!";
    }

    tp2020111202.png

  • preg_grep():返回匹配模式的数组条目

    1
    2
    3
    4
    5
    $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()字符串的替换函数

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

    字符串替换函数

    preg_replace:执行一个正则表达式的搜索和替换

  • 示例1:替换HTML标记

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //可以匹配所有HTML标记的开始和结束的正则表达式
    $pattern = "/<[\/\!]*?[^<>]*?>/is";

    //声明一个带有多个HTML标记的文本
    $text = "这个文本中有<b>粗体</b>和<u>带有下画线</u>以及<i>斜体</i>
    还有<font color='red' size='7'>带有颜色和字体大小</font>的标记";
    echo $text;
    echo "<br><br>";
    //将所有HTML标记替换为空,即删除所有HTML标记
    echo preg_replace($pattern, "", $text);
    echo "<br><br>";
    //通过第四个参数传入数字2,替换前两个HTML标记
    echo preg_replace($pattern, "", $text, 2);

    tp2020111203.png

  • 示例2:替换日期格式

    1
    2
    3
    4
    $pattern = "/(\d{2})\/(\d{2})\/(\d{4})/"; //日期格式的正则表达式
    $text="今年国庆节放假日期为10/01/2012到10/07/2012共7天。"; //带有两个日期格式的字串
    echo preg_replace($pattern, "\\3-\\1-\\2", $text); //将日期替换为以“-”分隔的格式
    echo preg_replace($pattern, "\${3}-\${1}-\${2}",$text); //将“\\1”改为“\${1}”的形式

    tp2020111204.png

    str_replace : 子字符串替换

    该函数返回一个字符串或者数组。通过$count参数指定来获取替换的次数。

  • 示例1:

    1
    2
    3
    4
    5
    6
    7
    8
    //声明包含多个“LAMP”字符串的文本,也包含小写的“lamp”字符串
    $str="LAMP是目前最流行的WEB开发平台;<br>LAMP为B/S架构软件开发的黄金组合;<br>LAMP每个成员都是开源软件;<br>lampBrother是LAMP的技术社区。<br>";
    //区分大小写的将“LAMP”替换为“Linux+Apache+MySQL+PHP”,并统计替换次数
    echo str_replace("LAMP", "Linux+Apache+MySQL+PHP",$str, $count);
    echo "区分大小写时共替换".$count."次<br>"; //替换4次
    //不区分大小写的将“LAMP”替换为“Linux+Apache+MySQL+PHP”,并统计替换次数
    echo str_ireplace("LAMP", "Linux+Apache+MySQL+PHP", $str,$count);
    echo "不区分大小写时共替换".$count."次<br>"; //替换5次

    tp2020111205.png

  • 示例2:

    1
    2
    3
    4
    5
    6
    7
    8
    //元音字符数组
    $vowels = array("a", "e", "i", "o", "u", "A", "E", "I", "O", "U");
    //将第三个参数中的字符串,搜索到的数组中的元素值都被替换为空,区分大写小替换
    echo str_replace($vowels, "", "Hello World of PHP"); //输出: Hll Wrld f PHP
    //元音字符数组
    $vowels = array("a", "e", "i", "o", "u");
    //将第三个参数中的字符串,搜索到的数组中的元素值都被替换为空,不区分大写小替换
    echo str_ireplace($vowels, "", "HELLO WORLD OF PHP"); //输出:HLL WRLD F PHP

    字符串的分割与连接

    preg_split:通过一个正则表达式分隔字符串

  • PREG_SPLIT_NO_EMPTY:返回分隔后的非空部分

  • PREG_SPLIT_DELIM_CAPTURE:用于分隔的模式中的括号表达式将被捕获并返回. 

  • PREG_SPLIT_OFFSET_CAPTURE:返回附加字符串偏移量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //按任意数量的空格和逗号分隔字符串,其中包含" ", \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:使用一个字符串分割另一个字符串

    array explode(string $separator,string $string[,int $limit])
    此函数返回由字符串组成的数组,每个元素都是 string的一个子串,它们被字符串 separator作为边界点分割出来。 其中$limit是指定对大分割个数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $lamp = "Linux Apache MySQL PHP";   //声明一个字符串$lamp,每个单词之间使用空格分割
    $lampbrother = explode(" ", $lamp); //将字符串$lamp使用空格分割,并组成数组返回

    $password = "redhat:*:500:508::/home/redhat:/bin/bash"; //将Linux中的用户文件的一行提出
    //按“:”分割7个子串,并存放对对应的变量中.
    list($user, $pass, $uid, $gid, , $home, $shell) = explode(":", $password);

    //声明字符串$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:使用一个子串组装一个数组。

    string implode(string $glue,array $pieces)
    pieces数组中的每个值,使用glue作为分隔符组装成一个子串并返回。

    1
    2
    3
    $lamp = array("Linux", "Apache", "MySQL", "PHP");
    echo implode("+", $lamp); //使用加号连接后输出Linux+Apache+MySQL+PHP
    echo join("+++", $lamp); //使用三个加号连接后输出Linux+++Apache+++MySQL+++PHP