正则表达式简介
正则表达式是用于描述字符排列和匹配模式的一种语法规则。它主要用于字符串的模式分割、匹配、查找及替换操作。到目前为止,我们前面所用过的精确(文本)匹配也是一种正则表达式。
在PHP中,正则表达式一般是由正规字符和一些特殊字符(类似于通配符)联合构成的一个文本模式的程序性描述。
PHP中,正则表达式有三个作用:
1.匹配,也常常用于从字符串中析取信息。
2.用新文本代替匹配文本。
3.将一个字符串拆分为一组更小的信息块。
一个正则表达式中至少包含一个原子。
在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}$/
正则表达式的语法规则
1.定界符
在程序语言中,使用与Perl兼容的正则表达式,通常都需要将模式表达式放入定界符之间,如“/”。
作为定界符常使用斜线“/”,如“/apple/”。用户只要把需要匹配的模式内容放入定界符之间即可。作为定界的字符也不仅仅局限于“/”。除了字母、数字和斜线“\”以外的任何字符都可以作为定界符,像“#”、“|”、“!”等都可以的。
2.原子
原子是正则表达式的最基本的组成单元,而且在每个模式中最少要少包含一个原子。
原子是由所有那些未显示指定为元字符的打印和非打印字符组成,具体分为5类:
2.1 普通字符作为原子
普通字符是编写正则表达式时最常见的原子了,包括所有的大写和小写字母字符、所有数字等。例如,a-z、A-Z、0-9。
/5/ –用于匹配字符串中是否有5这个字符出现
/php/ –用于匹配字符串中是否有PHP字符串出现
2.2 一些特殊字符和元字符作为原子
任何一个符号都可以作为原子使用,但如果这个符号在正则表达式中有一些特殊意义,我们就必须使用转义字符“\”取消它的特殊意义,将其变成一个普通的原子。例如,所有标点符号以及一些其他符号,双引号“””、单引号“’”、“*”、“+”、“.”等,如果当原子就必须像\”、\’、\+和\.这样使用。
/\./ –用于匹配字符串中是否有英文的“.”出现
/\<br\/\>/ –用于匹配字符串中是否有HTML的<br/>标记字符串出现
2.3 一些非打印字符作为原子
所谓的非打印字符,是一些在字符串中的格式控制符号,例如空格、回车及制表符号等。例如下表所示列出了正则表达式中常用的非打印字符及其含义。
2.4 使用“通用字符类型”作为原子
前面介绍的不管是打印字符还是非打印字符作为原子,都是一个原子只能匹配一个字符。而有时我们需要一个原子可以匹配一类字符,例如,匹配所有数字不是一个数字,匹配所有字母而不是一个字母,这时就要使用“通用字符类型”了。例如下表所示列出了正则表达式中常用“通用字符类型”及其含义。
2.5 自定义原子表([])作为原子
虽然前面介绍过“类原子”,可以代表一组原子中的一个,但系统只给我们提供了上表中介绍的6个“类原子”。因为代表某一类的原子太多了,系统不能全都给提供出来,例如数字中的奇数(1、3、5、7、9、)、字母中的元音字母(a、e、i、o、u)等。所以就需要我们可以自己定义出特定的“类原子”,使用原子表“[]”就可以定义一组彼此地位平等的原子,且从原子表中仅选择一个原子进行匹配
3.元字符
利用Perl正则表达式还可以做另一件有用的事情,这就是使用各种元字符来搜索匹配。所谓元字符,就是用于构建正则表达式的具有特殊含义的字符,例如的“*”、“+”、“?”等。在一个正则表达式中,元字符不能单独出现,它必须是用来修饰原子的。如果要在正则表达式中包含元字符本身,使其失去特殊的含义,则必须在前面加上“\”进行转义。正则表达式的元字符如下表所示。
构造正则表达式的方法和创建数学表达式的方法相似,就是用多种元素符与操作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。元字符是组成正则表达式的最重要部分,下面将这些元字符分为几类分别讲解。
3.1 字符串边界限制
在某些情况下,需要对匹配范围进行限定,以获得更准确的匹配结果。“^”和“$”分别指定字符串的开始和结束。
例如,在字符串“Tom and Jerry chased each other in the house until tom’s uncel come in”中
元字符“^”或“\A” 置于字符串的开始确保模式匹配出现在字符串首端;
/^Tom/ 元字符“$”或“\Z” 置于字符串的结束,确保模式匹配出现字符串尾端。
/in$/ 如果不加边界限制元字符,将获得更多的匹配结果。
/^Tom$/精确匹配 /Tom/模糊匹配
3.2 单词串边界限制
在使用各种编辑软件的查找功能时,可以通过选择“按单词查找”获得更准确的结果。正则表达式中也提供类似的功能。
例如:在字符串“This island is a beautiful land”中 元字符“\b”对单词的边界进行匹配;
/\bis\b/ 匹配单词“is”,不匹配“This”和“island”。
/\bis 匹配单词“is”和“island”中的“is”,不匹配“This” 元字符“\B”对除单词边界以外的部分进行匹配。
/\Bis\B/ 将明确的指示不与单词的左、右边界匹配,只匹配单词的内部。所以在这个例子中没有结果。
/\Bis 匹配单词“This”中的“is”
3.3 重复匹配
正则表达式中有一些用于重复匹配某些原子的元字符:“?”、“*”、“+”。他们主要的区别是重复匹配的次数不同。
元字符“?”:表示0次或1次匹配紧接在其前的原子。 例如:/colou?r/匹配“colour”或“color”。
元字符“*”:表示0次、1次或多次匹配紧接在其前的原子。 例如:/zo*/可以匹配z、zoo
元字符“+”:表示1次或多次匹配紧接在其前的原子。 例如:/go+gle/匹配“gogle”、“google”或“gooogle”等中间含有多个o的字符串。
3.4 任何一个字符
元字符“.”匹配除换行符外任何一个字符。
相当于:[^\n](Unix系统)或[^\r\n](windows系统)。 例如:/pr.y/可以匹配的字符串“prey”、“pray”或“pr%y”等。
通常可以使用“.*”组合来匹配除换行符外的任何字符。在一些书籍中也称其为“全匹配符” 或 “单含匹配符”。
例如: /^a.*z$/表示可以匹配字母“a”开头,字母“z”结束的任意不包括换行符的字符串。
/.+/ 也可以完成类似的匹配功能所不同的是其至少匹配一个字符。
/^a.+z$/ 匹配“a%z”不匹配字符串“az”。
3.5 重复匹配
元字符“{ }”准确地指定原子重复的次数,指定所匹配的原子出现的次数。
“{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。
3.6 元子表
原子表”[]”中存放一组原子,彼此地位平等,且仅匹配其中的一个原子。如果想匹配一个 ”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>”等
3.8 模式选择符
元字符“|”又称模式选择符。在正则表达式中匹配两个或更多的选择之一。
例如: 在字符串“There are many apples and pears.”中,/apple|pear/在第一次运行时匹配“apple”;
再次运行时匹配“ pear”。也可以继续增加选项,如: /apple|pear|banana|lemon/
3.9 模式单元
元字符“()”将其中的正则表达式变为原子(或称模式单元)使用。与数学表达式中的括号类似,“()”可以做一个单元被单独使用。
例如: /(Dog)+/匹配的“Dog”、“DogDog”、“DogDogDog”,因为紧接着“+”前的原子是元字符“()”括起来的字符串“Dog”。
/You (very )+ old/匹配“You very old”、“You very very old”
/Hello (world|earth)/匹配“Hello world”、“Hello earth” 一个模式单元中的表达式将被优先匹配或运算。
3.10 重新使用模式单元
系统自动将模式单元“()”中的匹配依次存储起来,在需要时可以用“\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/。
4. 模式修改符
模式修正符号在正则表达式定界符之外使用(最后一个斜线“/”之后),例如“php/i”。其中“/php/”是一个正则表达式的模式,而“i”就是修正此模式所使用的修正符号,用来匹配时不区分大小写。模式修正符可以调整正则表达式的解释,扩展了正则表达式在匹配、替换等操作时的某些功能,而且模式修正符号也可以组合使用,更增强了正则表达式的处理能力。例如“/php/Uis”则是使用“U”、“i”和“s”三个模式修正符组合在一起使用。模式修正符对编写简洁而简小的表达式大有帮助,在下面的表格中,列出了一些常用的模式修正符及其功能说明,如下表所示。
特别说明:4.1 模式修正符 U 结束贪婪模式,如下面示例
$pattern = '/<b>.*<\/b>/U'; $str = '<b>welcom</b> <b>to</b> <b>China</b>'; $count = preg_match($pattern, $str, $matches); print_r($matches); //结果是 welcome,而不是默认贪婪模式的全部匹配 //另外还可以使用 $pattern = '/<b>.*?</b>/'; 这种方式取消贪婪模式
与Perl兼容的正则表达式函数
1.正则匹配
函数preg_match() --执行一个正则表达式匹配
搜索subject与pattern给定的正则表达式的一个匹配.
函数preg_match_all() --执行全局正则表达式匹配
搜索subject中所有匹配pattern给定正则表达式 的匹配结果并且将它们以flag指定顺序输出到matches中. 参数flags是指定matches的数组格式。
函数preg_grep() --返回匹配模式的数组条目
返回给定数组input中与模式pattern 匹配的元素组成的数组。
2.正则搜索替换
preg_replace —执行一个正则表达式的搜索和替换
mixed preg_replace ( mixed $pattern , mixed $replacement,mixed $subject [,int $limit = -1])
搜索subject中匹配pattern的部分, 以replacement进行替换.
preg_replace_callback($pattern, "myfun", $subject); 回调函数替换
实例如下
function sks_replace($ms) { global $replace, $j; $str = $replace[$j]; $str = '<span class="list_item_time">'. $str. '</span>'; $j++; return $str; } $replace = array("2017-9-18","2017-10-18","2017-11-20","2017-12-05","2018-01-18"); $pattern = '/<span class=\"list_item_time\">(.*?)?<\/span>/i'; $j = 0; $new = preg_replace_callback($pattern,'sks_replace',$content);
使用心得
1.[^a]是匹配除了字符 a 以外的任意一个字符,如果我们想匹配排除多个字符或原子以外的任意一个字符,就可以在方括号内放入多个字符,比如 [^ajs] 这里就是分别排除字符 a、j、s三个字符以外的其他任意一个字符。
最近在使用这个方法时理解错了,导致搞了很久。下面分享下我的案例:
我在做一个服务器监控脚本时,需要实现一个匹配功能:匹配排除 .php 和 .js 后缀文件的其他任意后缀的文件。
正则表达式: ^.+\.[^p][^hj][^ps]$