Home > Article > Backend Development > PHP regular expression notes, php regular expression notes_PHP tutorial
On computers we often use (wildcards) to find the files we need, for example: *.doc
, where *
means matching zero or more characters. Regular expressions are also a tool used for text matching, but they are more powerful. To quote a sentence from the PHP manual: A regular expression is a pattern that matches a target string from left to right. Most characters themselves represent a pattern that matches themselves.
A few simple examples are given below to give you a preliminary understanding of regular expressions.
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}
In the above example, b
, .
, *
, d
, {2}
all have special meanings, which will be explained below.
There are two types of regular expressions supported in PHP: POSIX and PCRE. As of PHP 5.3.0, POSIX regular expression extensions are deprecated. Therefore, the following discussions are based on the PCRE mode. Click to view the differences from POSIX regular expressions and the differences from perl.
When using the PCRE function, the pattern needs to be enclosed by delimiters. The delimiter can be any non-alphanumeric, non-backslash, or non-whitespace character. Commonly used delimiters are forward slash /
, hash symbol #
and negation symbol ~
. The following examples are all patterns using legal delimiters.
/foo bar/ #^[^0-9]$# +php+ %[a-zA-Z0-9_-]%
If a delimiter needs to be matched within a pattern, it must be escaped with a backslash. If delimiters occur frequently within the pattern, a better option is to use other delimiters to improve readability. Example:
/http:\/\// #http://#
The power of regular expressions comes from their ability to select and repeat patterns. Some characters are given special meanings so that they no longer simply represent themselves. The encoded characters with special meanings in the pattern are called metacharacters .
There are two different metacharacters: one that can be used anywhere outside square brackets in the pattern, and one that needs to be used inside square brackets.
The metacharacters used outside square brackets are as follows:
代码 | 说明 |
---|---|
/ | 一般用于转义字符 |
^ | 断言目标的开始位置(或在多行模式下是行首) |
$ | 断言目标的结束位置(或在多行模式下是行尾) |
. | 匹配除换行符外的任何字符(默认) |
[ | 开始字符类定义 |
] | 结束字符类定义 |
| | 开始一个可选分支 |
( | 子组的开始标记 |
) | 子组的结束标记 |
? | a:作为量词,表示 0 次或 1 次匹配。b:位于量词后面用于改变量词的贪婪特性。 |
* | 量词,0 次或多次匹配 |
量词,1 次或多次匹配 | |
{ | 自定义量词开始标记 |
} | 自定义量词结束标记 |
The part in the square brackets in the pattern is called the "character class". The only metacharacters available within a character class are:
代码 | 说明 |
---|---|
转义字符 | |
^ | 仅在作为第一个字符(方括号内)时,表明字符类取反 |
- | 标记字符范围 |
Example:
baw*b
matches words starting with the letter a, first b at the beginning of a word, then the letter a, and then any number of any word characters (word characters refer to any letters, numbers, and underscores ) w*, and finally b at the end of the word. d
Matches 1 or more consecutive numbers. ^d{5,12}$
matches 5 to 12 digits. Because ^ and $ are used, the entire input string must be used to match d{5,12}, which means the entire input must be 5 to 12 numbers. Backslash has four uses. For details, click Escape Sequence (Backslash)
[1] is used as an escape character. For example, if you want to match a *
character, you need to write it as *
in the pattern. This applies when a character would have a special meaning without escaping. However, for non-alphanumeric characters, it is always safe to add a backslash in front of it to declare that it represents itself when it is needed for original text matching. If you want to match a backslash, use \
in the pattern.
Backslashes have special meaning in both single- and double-quoted strings, so to match a backslash, the pattern must be written as \
. The reason: first it is used as a string, and the backslashes will be escaped. Finally the regular expression engine also considers backslashes to be escaped. Therefore, 4 backslashes are needed to match one backslash.
[2] Provides a control method for visible encoding of non-printing characters
【3】Used to describe specific character classes
代码 | 说明 |
---|---|
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】Some simple assertions. An assertion specifies a condition that must be matched at a specific position; they do not consume any characters from the target string. Backslash assertions include:
b
Word boundariesB
Non-word boundaryA
The starting position of the target (independent of multiline mode) Z
The end position of the target or the newline character at the end (independent of multiline mode) z
The end position of the target (independent of multiline mode) G
First matching position in target 代码 | 说明 |
---|---|
* | 重复零次或更多次,等价于 |
重复一次或更多次,等价于 | |
? | 重复零次或一次,等价于 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
默认情况下,量词都是”贪婪”的,也就是说,它们会在不导致模式匹配失败的前提下,尽可能多的匹配字符(直到最大允许的匹配次数)。然而,如果一个量词紧跟着一个 ?
标记,它就会成为懒惰(非贪婪)模式, 它不再尽可能多的匹配,而是尽可能少的匹配。
下面直接看示例,理解“贪婪”和“非贪婪”模式是怎么回事。
对于字符串 "aa<span class="nt"><div></span>test1<span class="nt"></div></span>bb<span class="nt"><div></span>test2<span class="nt"></div></span>cc" 正则表达式 "<span class="nt"><div></span>.*<span class="nt"></div></span>" 匹配结果 "<span class="nt"><div></span>test1<span class="nt"></div></span>bb<span class="nt"><div></span>test2<span class="nt"></div></span>" 正则表达式 "<span class="nt"><div></span>.*?<span class="nt"></div></span>" 匹配结果 "<span class="nt"><div></span>test1<span class="nt"></div></span>"
关于更多“贪婪”和“非贪婪”模式的介绍可查阅 http://php.net/manual/zh/regexp.reference.repetition.php
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}
。
竖线字符用于分离模式中的可选路径。比如模式 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 位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。
使用分枝条件时,要注意各个条件的顺序
正则表达式在不同的模式修饰符下匹配出的结果有可能不相同。它的语法是 :(?修饰符)
比如,(?im)
设置表明多行大小写不敏感匹配。同样可以用它来取消这些设置,比如 (?im-sx)
设置了 “PCRE_CASELESS”,”PCRE_MULTILINE”,但是同时取消了 “PCRE_DOTALL” 和 “PCRE_EXTENDED”。如果一个字母即出现在 -
之前, 也出现在 -
之后,这个选项被取消设置。
下面紧例举简单的示例,想要了解更多可点击 内部选项设置 和 模式修饰符
示例:/ab(?i)c/
仅仅匹配 ”abc” 和 ”abC”
子组通过圆括号分隔界定,并且它们可以嵌套。
示例:
字符串:"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