Skip to content

正则表达式

向军大叔每晚八点在 抖音bilibli 直播

xj-small

正则表达式被用来检索或替换那些符合某个模式的文本内容。许多程序设计语言都支持利用正则表达式进行字符串操作。例如:JavaScript、PHP、ASP、JAVA、Perl、C#、.NET、ColdFusion、Python、Visual Basic、MYSQL、LINUX、VI 编辑器等等语言都支持正则表达式。

简单来说正则表达式就是完成字符串的增、删、改、查。

定界符

正则表达式语句需要由分隔符(定界符)闭合包裹,分隔符可以使任意非字母数字, 非反斜线, 非空白字符

经常使用的分隔符是正斜线(/), hash 符号(#) 以及取反符号(~)。

建议使用//做为定界符,因为与 js 一致。

$status =  preg_match('/houdunren/', 'houdunren.com');
var_dump($status);

元字符

元字符是正则表达式中的最小元素,只代表单一(一个)字符。下面是元字符列表

元字符说明范围
\d匹配任意一个数字[0-9]
\D与除了数字以外的任何一个字符匹配[^0-9]
\w与任意一个英文字母,数字或下划线匹配[a-zA-Z_0-9]
\W除了字母,数字或下划线外与任何字符匹配[^a-za-z_0-9]
\s与任意一个空白字符匹配[\n\f\r\t\v]
\S与除了空白符外任意一个字符匹配[^\n\f\r\t\v]
\n换行字符
\t制表符
var_dump(preg_match('/\d/', '1'));
var_dump(preg_match('/\D/', 'h'));
var_dump(preg_match('/\w/', '_'));
var_dump(preg_match('/\W/', '@'));
var_dump(preg_match('/\s/', ' '));
var_dump(preg_match('/\S/', 'h'));
var_dump(preg_match('/\n/', "\n"));
var_dump(preg_match('/\n/', '
'));
var_dump(preg_match('/\t/', "\t"));

原子表

在一组字符中匹配某个元字符,在正则表达式中通过元字符表来完成,就是放到方括号中。

原子表说明
[]只匹配其中的一个原子
[^]只匹配"除了"其中字符的任意一个原子
[0-9]匹配 0-9 任何一个数字
[a-z]匹配小写 a-z 任何一个字母
[A-Z]匹配大写 A-Z 任何一个字母
.点在正则中表示除换行符外的任意字符

匹配聊了 678外的任何字符

$status = preg_match('/[^678]/', 678);
var_dump($status);

匹配大小写字母

$status = preg_match('/[a-zA-Z]/', 'a');
var_dump($status);

. 匹配字符

$status = preg_match('/./', 'houdunren');
var_dump($status);

下面是通过原子表拆分字符串

$str ="1.jpg@2.jpg@3.jpg#4.jpg";
$arr = preg_split('/[@#]/',$str); //按正则表达式拆分字符串
print_r($arr); //输出结果 Array ( [0] => 1.jpg [1] => 2.jpg [2] => 3.jpg [3] => 4.jpg )

原子组

  • 如果一次要匹配多个元子,可以通过元子组完成
  • 原子组与原子表的差别在于原子组一次匹配多个元子,而原子表则是匹配成功表中的一个元字符就可以
  • 元字符组用()表示

下面是使用正则表达式将houdunwang 字符中的 houdun 描红。

$str = "官网www.houdunwang.com 论坛http://bbs.houdunwang.com,我在网名叫houdun";
$preg = "/(houdun)wang/is";
$newStr= preg_replace($preg, '<span style="color:#f00">\1</span>wang', $str);
echo $newStr;

也可以使用$n 的形式获取组数据,下例是将超链接替换为使用 h2 标签包裹

$content = <<<str
<a href="https://www.houdunren.com">后看人</a>
<a href="https://www.hdcms.com">hdcms</a>
str;
echo preg_replace('/<a.*?>(.*?)<\/a>/i', '<h2>$1</h2>', $content);

#执行结果
<h2>后看人</h2>
<h2>hdcms</h2>

分组别名

有时要为匹配到的结果使用字符串命名(别名),而不是使用默认的数字索引,使用(?<hd>)形式可以实现

preg_match('/(?<hd>\d+)/', 'houdunren2010', $match);
print_r($match);

#匹配结果
Array ( [0] => 2010 [hd] => 2010 [1] => 2010 )

也可以写成引号的形式,但要注意引号的嵌套问题

preg_match("/(?'hd'\d+)/", 'houdunren2010', $match);
print_r($match);

#匹配结果
Array ( [0] => 2010 [hd] => 2010 [1] => 2010 )

不记录分组

如果希望分组匹配,但结果中不保存分组结果时使用 (?:exp),下例中的结果中将不包括分组数据

preg_match('/(?:\d+)/', 'houdunren2010', $match);
print_r($match);

#匹配结果
Array ( [0] => 2010 )

断言匹配

断言匹配的内容不会保存到结果中,是对内容前后位置的限制操作。

正向先行断言

匹配指定内容前面的内容时使用 (?=exp),下面是匹配数字 2 前面的内容

preg_match("/.*(?=2)/", 'houdunren2010', $match);
print_r($match);

#匹配结果
Array ( [0] => houdunren )

正向后行断言

匹配指定内容后面的内容时使用 (?<=exp),下面是匹配数字 2 前面的内容

preg_match("/(?<=2).*/", 'houdunren2010', $match);
print_r($match);

#匹配结果
Array ( [0] => 010 )

负向先行断言

使用(?!exp)匹配后面不能是指定内容,下面匹配后面不是#的四位数值

preg_match_all("/\d{4}(?!#)/", '2030# 2022年', $match);
print_r($match);

#匹配结果
Array ( [0] => Array ( [0] => 2022 ) )

负向后行断言

负向先行断言 指使用(?<!exp)匹配前面不能是指定内容,下面匹配前面不是#的四位数值

preg_match_all("/(?<!#)\d{4}/", '#2030 2022年', $match);
print_r($match);

#匹配结果
Array ( [0] => Array ( [0] => 2022 ) )

边界匹配

如果想匹配字符的边界,边界包括空格、标点符号、换行等,可以使用正则表达式的匹配字符边界修饰符如下。

符号说明
^匹配字符串的开始
$匹配字符串的结束,忽略换行符
$status = preg_match('/^houdunren$/', 'houdunren');
var_dump($status); //返回真

选择修释

| 这个符号带表选择修释符,也就是 | 左右两侧有一个匹配到就可以。

下面是通过选择修释符将域名修改为 houdunren

$str = "http://www.baidu.com与新浪网http://www.sina.com";
$preg = "/\.(baidu|sina)\./is";
echo preg_replace($preg, '.houdunren.', $str);

匹配域名后缀

$str = '来了,老弟!有问题就上 houdunren.com 或 houdunwang.com 我们在那里等你';
$preg = '/(houdunren|houdunwang)\.com/';
$replace = '<a href="http://www.\1.com">\1.com</a>';
echo preg_replace($preg, $replace, $str);

重复匹配

如果要重复匹配一些内容时我们要使用重复匹配修饰符,包括以下几种。

因为正则最小单位是元字符,而我们很少只匹配一个元字符如 a、b 所以基本上重复匹配在每条正则语句中都是必用到的内容

符号说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复 n 次
{n,}重复 n 次或更多次
{n,m}重复 n 到 m 次
var_dump(preg_match('/^.*$/', '')); //* 零个及空字符串也是可以的
var_dump(preg_match('/^[0-9]+$/', '1976')); // 返回真
var_dump(preg_match('/^9?$/', '99')); // 返回假
var_dump(preg_match('/^9{2}$/', '99')); // 返回真
var_dump(preg_match('/^[0-9]{2,}$/', '123')); //返回真
var_dump(preg_match('/^[0-9]{2,3}$/', '1234')); // 返回假,只能2~3位

下面匹配域名操作

$web = 'sina.com';
var_dump(preg_match('/^[a-z-0-9-]+\.(com|net|com\.cn|org|cn)$/', $web));

下面是把 h1标签内容加上超链接

$str = <<<html
<h1>hello houdunren</h1>
html;
echo preg_replace('/<h1>(.+)<\/h1>/', '<a href="http://www.houdunren.com">\1</a>', $str);

禁止贪婪

正则表达式在进行重复匹配时,默认是贪婪匹配模式,也就是说会尽量匹配更多内容,但是有的时候我们并不希望他匹配更多内容,这时可以通过?进行修饰来禁止重复匹配。

符号说明
*?重复任意次,但尽可能少重复
+?重复 1 次或更多次,但尽可能少重复
??重复 0 次或 1 次,但尽可能少重复
{n,m}?重复 n 到 m 次,但尽可能少重复
{n,}?重复 n 次以上,但尽可能少重复
$str = '123456';
preg_match('/\d+?/',$str,$matches);
print_r($matches);

因为增加了 ? 所以只匹配数字 1

下面是使用禁止贪婪符替换将 h1 标签内容倾斜处理

$str = "<h1>你好</h1><h1>向军大叔</h1>";
$preg = '/<h1>(.*?)<\/h1>/';
$replace = '<h1><em>\1</em></h1>';
echo preg_replace($preg,$replace,$str);

下面是替换 h1-h6 标签内容,就不需要使用禁止贪婪符了

$str = "<h1>你好</h1><h2>向军大叔</h2>";
$preg = '/<h([1-6])>(.*)<\/h\1>/';
$replace = '<h\1><em>\2</em></h\1>';
echo preg_replace($preg,$replace,$str);

模式修正

正则表达式在执行时会按他们的默认执行方式进行,但有时候默认的处理方式总不能满足我们的需求,所以可以使用模式修正符更改默认方式。

符号说明
i不区分大小写字母的匹配
s将字符串视为单行,换行符做普通字符看待,使“.” 匹 配任何字符
x忽略空白及#符号,根据此特性可以添加正则注释
m^与$符匹配按行匹配
A强制从字符串开始匹配(多行时默认以每行开始设置)
D以$结尾时不允许后面有换行(使用\m 时无效)
u匹配内容按 utf-8 处理,可以用来匹配 utf-8 的中文字符

\i 修正符不匹分大小示例

$str = 'https://www.HoudunREN.com 后盾人';
$preg = '/https:\/\/www.houdunren.com/i';
$replace= '<a href="\0">\0</a>';
echo preg_replace($preg,$replace,$str);

\s 将字符串示为单行操作

$preg = '#(<h1>)(.*?)(</h1>)#s';
preg_match_all($preg,$str,$matches);
//print_r($matches);

$replace = '\1<span style="color:red">\2\3';
echo preg_replace($preg,$replace,$str);

\m 使用 ^ 与 $ 按单行操作文本

$str = <<<php
#1 2022-02-12
后盾人
@#100
雅虎网
#2 2033-09-11
向军大叔
php;
$preg = '/^#\d+.*[\r|\n]$/m';
preg_match_all($preg, $str, $matches);
print_r($matches);

\x 字符忽略空白和添加正则注释

  1. 添加注释

    $str='houdunren.com';
    $preg = '/^h
       # 这是匹配以 h 开始的内容
    /x';
    echo preg_replace($preg,'',$str);
  2. 当设置了\x 后# 会被忽略,如果正则中使用#需要转义处理

    $str=<<<php
    #1
    后盾人#1
    #2
    向军大叔#2
    php;
    $preg = '/^
    \#\d  #匹配以数字开始
    +.* $ #后跟任何字符
    /mx';
    echo preg_replace($preg,'',$str);

\U 与?功能相似意为禁止贪婪匹配

$str = <<<php
<h1>后盾人</h1>
<h1>向军大叔</h1>
php;
;
$preg = '#<h1>.*</h1>#sU';
preg_match_all($preg,$str,$matches);
print_r($mat
ches);

\A 与 ^限定符使用效果相似,必须以目标字符串开始。下面是验证邮箱例子

$str = '2300071698@qq.com';
$preg = '/\w+@[\w\.]+/A';
preg_match_all($preg,$str,$matches);
print_r($matches);

\D 修正符使用不允许以换行结束

$str = <<<php
3a\n
php;
$preg = '/\d+a$/D';
preg_match_all($preg,$str,$matches);
print_r($matches);

常用函数

preg_match

获取第一个匹配的内容

$str= '1@2@3';
preg_match('/\d+/',$str,$matches);
print_r($matches);

preg_match_all

获取所有匹配的内容

$str= '1@2@3';
preg_match('/\d+/',$str,$matches);
print_r($matches);

preg_split

通过正则表达式拆分字符串

$str= '1@2#3';
$arr = preg_split('/@|#/',$str);
print_r($arr);

preg_replace

通过正则表达式替换

$str= '1@2#3';
echo preg_replace('/@|#/','-',$str);

preg_replace_callback

使用回调函数进行替换操作

$str = '1@2@3';
echo preg_replace_callback('/\d+/',function ($matches) {
	return $matches[0]+100;
}, $str);