PHP 正则表达式笔记,php正则表达式笔记


什么是正则表达式

在电脑上我们经常会使用(通配符)找出我们需要的文件,例如:*.doc ,这里的 * 代表匹配零个或多个字符。正则表达式也是用来进行文本匹配的工具,只不过它更加强悍。引用 PHP 手册里的一句话:正则表达式是一个从左到右匹配目标字符串的模式,大多数字符自身就代表一个匹配 它们自身的模式。

下面给出几个简单例子,使对正则表达式有个初步的理解。

hi  //匹配英文字符(忽略大小写) hi , HI , Hi , hI
登录后复制
\bhi\b  //匹配英文单词 hi  '\b'是正则里的一特殊字符(一种断言),表示单词边界
登录后复制
\bhi\b.*\bLucy\b  //匹配如:'hi my name is Lucy'  '.' 表示匹配除换行符以外的任意字符  '*' 是量词,表示重复零次或更多次
登录后复制
0\d{2}-\d{8}  //匹配如: 020-12345678  '\d' 匹配一个数字(0-9)    '{n}' 重复n次,如{2} {8}
登录后复制

上面例子中的 \b , . , * , \d , {2} 都有特殊含义,在下文会有说明。

PHP 中正则语法

1.简介

在 PHP 里支持两种正则分别是 POSIX 和 PCRE 。自 PHP 5.3.0起,POSIX 正则表达式扩展被废弃。所以下文讨论的都是基于 PCRE 模式。可点击查看有关与 POSIX 正则表达式的不同和与 perl 的不同之处。

2.分隔符

当使用 PCRE 函数 的时候,模式需要由分隔符闭合包裹。分隔符可以使任意非字母数字、非反斜线、非空白字符。经常使用的分隔符是正斜线 / 、hash符号 # 以及取反符号 ~ 。下面的例子都是使用合法分隔符的模式。

/foo bar/
#^[^0-9]$#
+php+
%[a-zA-Z0-9_-]%
登录后复制

如果分隔符需要在模式内进行匹配,它必须使用反斜线进行转义。如果分隔符经常在模式内出现,一个更好的选择就是是用其他分隔符来提高可读性。例:

/http:\/\//
#http://#
登录后复制

3.元字符

正则表达式的威力源于它可以在模式中拥有选择和重复的能力。一些字符被赋予特殊的含义,使其不再单纯的代表自己,模式中的这种有特殊涵义的编码字符 称为元字符
共有两种不同的元字符:一种是可以在模式中方括号外任何地方使用的,另外一种是需要在方括号内使用的。

在方括号外使用的元字符如下:

/一般用于转义字符
^断言目标的开始位置(或在多行模式下是行首)
$断言目标的结束位置(或在多行模式下是行尾)
.匹配除换行符外的任何字符(默认)
[开始字符类定义
]结束字符类定义
|开始一个可选分支
(子组的开始标记
)子组的结束标记
?a:作为量词,表示 0 次或 1 次匹配。b:位于量词后面用于改变量词的贪婪特性。
*量词,0 次或多次匹配
+量词,1 次或多次匹配
{自定义量词开始标记
}自定义量词结束标记

模式中方括号内的部分称为“字符类”。 在一个字符类中仅有以下可用元字符:

\转义字符
^仅在作为第一个字符(方括号内)时,表明字符类取反
-标记字符范围

示例:

  • \ba\w*\b 匹配以字母 a 开头的单词,先是某个单词开始处 \b ,然后是字母 a ,然后是任意数量的任意单词字符(单词字符指的是任意字母、数字、下划线) \w* ,最后是单词结束处 \b 。
  • \d+ 匹配1个或更多连续的数字。
  • ^\d{5,12}$ 匹配为5位到12位数字,因为使用了 ^ 和 $ ,所以输入的整个字符串都要用来和 \d{5,12} 来匹配,也就是说整个输入必须是5到12个数字。

4.转义序列(反斜线)

反斜线 \ 有四种用法,详细可点击 转义序列(反斜线)

【1】作为转义字符,比如,如果你希望匹配一个 * 字符,就需要在模式中写为 \* 。这适用于一个字符在不进行转义会有特殊含义的情况下。 但是,对于非数字字母的字符,总是在需要其进行原文匹配的时候在它前面增加一个反斜线,来声明它代表自己,这是安全的。如果要匹配一个反斜线,那么在模式中使用 \\
反斜线在单引号字符串和双引号字符串中都有特殊含义,因此要匹配一个反斜线, 模式中必须写为 \\\\ 。其中的原因:首先它作为字符串,反斜线会进行转义。最后正则表达式引擎也认为反斜线是转义。因此,需要 4 个反斜线才可以匹配一个反斜线。

【2】提供了一种对非打印字符进行可见编码的控制手段

【3】用来描述特定的字符类

\d任意十进制数字
\D任意非十进制数字
\h任意水平空白字符(since PHP 5.2.4)
\H任意非水平空白字符(since PHP 5.2.4)
\s任意空白字符
\S任意非空白字符
\v任意垂直空白字符(since PHP 5.2.4)
\V任意非垂直空白字符(since PHP 5.2.4)
\w任意单词字符,单词字符指的是任意字母、数字、下划线。
\W任意非单词字符

【4】一些简单的断言。一个断言指定一个必须在特定位置匹配的条件,它们不会从目标字符串中消耗任何字符。反斜线断言包括:

  • \b 单词边界
  • \B 非单词边界
  • \A 目标的开始位置(独立于多行模式)
  • \Z 目标的结束位置或结束处的换行符(独立于多行模式)
  • \z 目标的结束位置(独立于多行模式)
  • \G 在目标中首次匹配位置

5.重复/量词

*重复零次或更多次,等价于
+重复一次或更多次,等价于
?重复零次或一次,等价于
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次

默认情况下,量词都是”贪婪”的,也就是说,它们会在不导致模式匹配失败的前提下,尽可能多的匹配字符(直到最大允许的匹配次数)。然而,如果一个量词紧跟着一个 ? 标记,它就会成为懒惰(非贪婪)模式, 它不再尽可能多的匹配,而是尽可能少的匹配。
下面直接看示例,理解“贪婪”和“非贪婪”模式是怎么回事。

对于字符串 "aa
test1
bb
test2
cc" 正则表达式 "
.*
" 匹配结果 "
test1
bb
test2
" 正则表达式 "
.*?
" 匹配结果 "
test1
"
登录后复制

关于更多“贪婪”和“非贪婪”模式的介绍可查阅 http://php.net/manual/zh/regexp.reference.repetition.php

6.字符类(方括号)

PHP手册中的描述:

  • 左方括号开始一个字符类的描述,并以方中括号结束。单独的一个右方括号没有特殊含义。如果一个右方括号需要作为一个字符类中的成员,那么可以将它写在字符类的首字符处(如果使用了 ^ 取反,那么是第二个)或者使用转义符。

  • 一个字符类在目标字符串中匹配一个单独的字符;该字符必须是字符类中定义的字符集合的其中一个, 除非使用了 ^ 对字符类取反。如果^需要作为一个字符类的成员,确保它不是该字符类的首字符,或者对其进行转义即可。

示例:

[aeiou]    //匹配所有的小写元音字母

[^aeiou]   //匹配所有非元音字母的字符

[.?!]      //匹配标点符号(.或?或!)
登录后复制

注意:^ 只是一个通过枚举指定那些不存在字符类之中的字符的便利符号。而不是断言, 它仍然会从目标字符串中消耗一个字符,并且如果当前匹配点在目标字符串末尾, 匹配将会失败。

轻松地指定一个字符范围,范围操作以 ASCII 整理排序。它们可以用于为字符指定数值,比如 [\000-\037]

[0-9]    //代表的含意与 '\d' 就是完全一致的
[a-z0-9A-Z_]    //完全等同于 '\w' 如果只考虑英文的话
登录后复制

下面是一个更复杂的表达式 \(?0\d{2}[) -]?\d{8}
这个表达式可以匹配几种格式的电话号码,像 (010)88886666,或 022-22334455 ,或 02912345678 等。
简单分析:首先是一个转义字符 \( ,它能出现 0 次或 1 次 ? ,然后是一个数字 0 ,后面跟着 2 个数字 \d{2} ,然后是 )- 或 “空格” 中的一个,它出现 0 次或 1 次,最后是 8 个数字 \d{8}

7.分支 ( | )

竖线字符用于分离模式中的可选路径。比如模式 gilbert|Sullivan 匹配 ”gilbert” 或者 ”sullivan”。竖线可以在模式中出现任意多个,并且允许有空的可选路径(匹配空字符串)。匹配的处理从左到右尝试每一个可选路径,并且使用第一个成功匹配的。如果可选路径在子组(下面定义)中,则”成功匹配”表示同时匹配了子模式中的分支以及主模式中的其他部分。

回看上文里的一个例子 \(?0\d{2}[) -]?\d{8} 这个正则也能匹配 010)12345678 或 (022-87654321 这样的 “不正确” 的格式。其实我们可以利用分支就能解决这个问题,如下:

\({1}0\d{2}\){1}[- ]?\d{8}|0\d{2}[- ]?\d{8} 这个表达式匹配 3 位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。

使用分枝条件时,要注意各个条件的顺序

8.内部选项设置

正则表达式在不同的模式修饰符下匹配出的结果有可能不相同。它的语法是 :(?修饰符)

比如,(?im) 设置表明多行大小写不敏感匹配。同样可以用它来取消这些设置,比如 (?im-sx) 设置了 “PCRE_CASELESS”,”PCRE_MULTILINE”,但是同时取消了 “PCRE_DOTALL” 和 “PCRE_EXTENDED”。如果一个字母即出现在 - 之前, 也出现在 - 之后,这个选项被取消设置。

下面紧例举简单的示例,想要了解更多可点击 内部选项设置 和 模式修饰符

示例:/ab(?i)c/ 仅仅匹配 ”abc” 和 ”abC”

9.子组(子模式)

子组通过圆括号分隔界定,并且它们可以嵌套。

示例:

字符串:"the red king"
正则表达式:((red|white) (king|queen))
匹配结果:array("red king", "red king", "red", "king")
描述:其中第 0 个元素是整个模式匹配的结果,后面的三个元素依次为三个子组匹配的结果。 它们的下标分别为 1, 2, 3。
登录后复制

经常我们会有一种需求需要使用子组进行分组,但又不需要(单独的)捕获它们。在子组定义的左括号后面紧跟字符串 ?: 会使得该子组不被单独捕获,并且不会对其后子组序号的计算产生影响。例如:

字符串:"the red king"
正则表达式:((?:red|white) (king|queen))
匹配结果:array("red king", "red king", "king")
登录后复制

为了方便简写,如果需要在非捕获子组开始位置设置选项, 选项字母可以位于 ?: 之间,比如:

(?i:saturday|sunday)
(?:(?i)saturday|sunday)
登录后复制

上面两种写法实际上是相同的模式。因为可选分支会从左到右尝试每个分支,并且选项没有在子模式结束前被重置,并且由于选项的设置会穿透对后面的其他分支产生影响,因此, 上面的模式都会匹配 ”SUNDAY” 以及 ”Saturday”。

再看一个匹配 IP 地址的正则 ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
相关文章 IP地址的正则表达式

结语

上文中涉及 PHP 正则表达式中常用的语法,有的语法没细说和涉及到的,如:模式修饰符、后向引用、断言、递归模式,等。你可以通过 PHP 手册查看这些内容。

提示:一般而言,对于同样的功能,正则表达式函数运行效率要低于字符串函数。如果应用程序较简单,那么就用字符串表达式。但是,对于可以通过单个正则表达式执行的任务来说,如果使用多个字符串函数,则是不对的。 ---- 摘自《PHP 和 MySQL Web 开放》一书。

参考资料

http://php.net/manual/zh/book.pcre.php
https://msdn.microsoft.com/zh-cn/library/d9eze55x%28v=vs.80%29.aspx
http://deerchao.net/tutorials/regex/regex.htm
http://tool.chinaz.com/regex/
http://www.regexlab.com/zh/regref.htm

09-15 11:55