PHP正则表达式简介

1. 什么是正则表达式

正则表达式(regular expression)是一组用来匹配模式的字符串,通过定义规则,来实现字符串的查找、替换和验证。举个例子,你可以用正则表达式来查找一段字符串中的数字;再比如说,可以将字符串中符合规则的子串替换成另外的字符;又或者,用正则表达式来验证用户输入的密码是否符合某种规范(比如长度6-8个字符,同时包含大小写字母和数字)。

不同语言的正则表达式有不同的语法,不过大体上都有普通字符、限定符、元字符、转义符号、修饰符等概念,下面简单讲解一下PHP中的正则表达式。

2.PHP的正则表达式

我们先看看一个简单的模式,/https?/i,其中,/表示分隔符,两个/中间的部分就是匹配的规则。最后面的i是修饰符,在这里表示不匹分大小写,修饰符不是必需的。https属于普通符号,\d属于元字符,代表着数字0-9

2.1 分隔符

常用的分隔符是正斜线(/),但除了字母、数字、空格、以及反斜线(\)外,其他符号都可以用作分隔符。比如:

/http:\/\//
#http:\\#

都是合法的PHP模式。可以看到,在模式中使用到分隔符的时候,必须用转义字符进行转义。所以如果分隔符经常在正则模式内出现,可以使用其他分隔符以便提高可读性。

2.2 元字符

正则表达式的强大源于它可以在模式中拥有选择和重复的能力。一些字符被赋予特殊的涵义,这些字符就是元字符。PHP中的元字符有以下这些:

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

在模式中,方括号[]内的部分称为字符类,表示一些字符的合集。在这一部分,有另外的元字符定义:

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

举个例子,/[a-z0-9,]/表示所有小写字母和数字和逗号,[^a-z,]表示除了小写字母和逗号之外的所有字符。

2.3 转义序列(反斜线)

反斜线\通常的作用是转义,即表明取消该字符所代表的特殊涵义。比如上面提到的元字符*,如果要匹配到星号*,而不是表示0或多次匹配,则可以写成\*,反斜线在这里就起到转义作用。
  \的第二个作用,是用来表示非打印字符,比如换行符\n,制表符\t等等。
  对于\的另一个重要的作用,则是用来描述特定的字符类:

字符 说明
\d 任意十进制数字
\D 任意非十进制数字
\w 任意单词字符(字母、数字、下划线)
\W 任意非单词字符
\s 任意空白字符
\S 任意非空白字符
2.4 量词

量词用于表示重复多次匹配,格式为{数字1,数字2},其中两个数值都必须小于 65536, 并且第一个数字必须小于等于第二个。如果数字2被省略{数字1,},则表示没有大限。如果连逗号都被省略{数字1},则表示确定次数的匹配。
  举例说明,/a{3,8}/表示匹配连续的3-8个a,/a{3,}/表示匹配大于等于3个a,/a{3}/表示匹配刚好3个a。常用的*,+,?分别等价于{0,},{1,},{0,1}

默认情况下,量词都是“贪婪”的,也就是说, 它们会在不导致模式匹配失败的前提下,尽可能多的匹配字符。然而,如果一个量词紧跟着一个 ?(问号) 标记,它就会成为懒惰(非贪婪)模式, 它不再尽可能多的匹配,而是尽可能少的匹配。
  例如,/<(.*)>/去匹配<head></head>会匹配到整个字符串,.*捕获出来的是head></head,而/<(.*?)>/去匹配<head></head>只会匹配到前面的<head>.*?捕获到的是head

2.5 修饰符

在分隔符后面,可以加修饰符,常用的修饰符有:

符号 说明
i 不区分大小写
s 模式中的圆点符号.匹配所有的字符,包括换行符
m 匹配多行,使^$分别匹配到每一行的行首和行尾

例如/abc/i可以匹配到abcAbcABC等字符串。

2.6 子组和可选路径以及后向引用

圆括号()中的部分即为子组,可以用来捕获其中的内容,也可以在其中加上|形成可选路径。比如/cat(abc)+/abc为一个整体,可以匹配到catabccatabcabc等。如果没有括号的话,/catabc+/就只能匹配到catabccc之类的字符串。再比如,/cat(abc|def)/中,|将子组分成两个可选的部分,能匹配到catabccatdef两个字符串。值得注意的是,|可以为空的字符串,比如/cat(abc|def|)/可以匹配到catabccatdefcat三个字符串。
  圆括号()中匹配到的内容,可以在后面通过反斜杠+数字的方式进行引用,这称之为后向引用。举例说明,/(\d{2})=\1/可以匹配到13=13/(\d{2})(\d{2})=\2\1/可以匹配到1234=3412,这里面\1\2表示前面捕获到的第1、第2个子组。
  如果想要在括号里只表示分组,而不需要捕获,可以在左括号后面紧跟字符串 ?:,例如/(?:\d{2})(\d{2})=\1/中只会捕获到后面括号的一组内容,所以后面只能跟\1,可以匹配到1234=34这样的字符串。

3. PHP的正则表达式的函数

3.1 preg_match

preg_match用于执行匹配正则表达式。其函数原型为:

1
preg_match($pattern,$subject [, &$matches [, $flags = 0 [, $offset = 0 ]]])

参数说明如下:

  • pattern : 要搜索的模式,字符串类型。
  • subject : 输入字符串。
  • matches : 可选参数,会被填充为搜索结果。其中matches[0]将包含完整模式匹配到的文本, matches[1] 将包含第一个捕获子组匹配到的文本,以此类推。
  • flags : 可选参数,$flags 可以被设置为 PREG_OFFSET_CAPTURE,如果传递了这个标记,对于每一个出现的匹配,返回时都会附加上字符串偏移量(相对于目标字符串的);
  • offset : 可选参数,用于指定从目标字符串的某个位置开始搜索(单位是字节)。

preg_match函数返回匹配次数,它的值将是0次(不匹配)或 1 次,因为preg_match在第一次匹配后将会停止搜索。

【示例】使用 preg_match() 函数搜索一个字符串。

1
2
echo preg_match('/([a-z])(\d{6})/', "a123456,b654321",$output);
print_r($output);

输出结果为:

1
2
3
4
5
6
1Array
(
[0] => a123456
[1] => a
[2] => 123456
)

可以看到preg_match在匹配到一次后就结束了。如果想匹配到所有的话,可以使用preg_match_all

1
2
echo preg_match_all('/([a-z])(\d{6})/', "a123456,b654321",$output);
print_r($output);

输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2Array
(
[0] => Array
(
[0] => a123456
[1] => b654321
)

[1] => Array
(
[0] => a
[1] => b
)

[2] => Array
(
[0] => 123456
[1] => 654321
)
)
3.2 preg_replace

preg_replace用于执行一个正则表达式的搜索和替换。其函数原型为:

1
preg_replace($pattern, $replacement, $subject [, $limit = -1 [, &$count]])

参数说明如下:

  • pattern : 要搜索的模式。可以使一个字符串或字符串数组。
  • replacement : 用于替换的字符串或字符串数组。如果这个参数是一个字符串,并且 pattern 是一个数组,那么所有的模式都使用这个字符串进行替换。如果 pattern 和 replacement 都是数组,每个 pattern 使用 replacement 中对应的元素进行替换。如果 replacement 中的元素比 pattern 中的少,多出来的 pattern 使用空字符串进行替换。
  • subject : 要进行搜索和替换的字符串或字符串数组。
  • limit : 可选参数,每个模式在每个 subject 上进行替换的最大次数。默认是 -1(无限)。
  • count : 可选参数,如果指定,将会被填充为完成的替换次数。

如果 subject 是一个数组,preg_replace() 返回一个数组,其他情况下返回一个字符串。

如果匹配被查找到,替换后的 subject 被返回,其他情况下返回没有改变的 subject。如果发生错误,返回 null 。

【示例】

1
2
3
4
5
6
//把数字替换成*号
echo preg_replace("/\d/","*","abc123"); //输出 abc***
//把[]替换成{}
echo preg_replace(["/\[/","/\]/"],["{","}"],"[abc]"); //输出 {abc}
//把=两边互换
echo preg_replace("/(.*)=(.*)/", "$2=$1", "key=val"); //输出 val=key

【写在最后】
正则表达式虽好,但也不必要凡事尽皆正则。如果你仅仅想要检查某个字符串是否包含另外一个字符串,不要使用preg_match,使用strpos会更快。若凡是看到字符串就想正则,就会像这么一个段子:

1
我遇到了一个问题,我觉得可以用正则表达式来解决。好了,我现在有两个问题了。

参考


PHP正则表达式简介
https://blog.supersource.top/regular_expression/
作者
看热闹的咸鱼
发布于
2022年6月6日
许可协议