PHP preg_match_all() 函数(保姆级教程)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观


二、前言:正则表达式与多模式匹配的重要性

在 PHP 开发中,正则表达式(Regular Expression,简称 regex)是处理文本、验证数据、提取信息的重要工具。而 preg_match_all() 函数作为 PHP 正则表达式家族的核心成员之一,能够一次性返回所有匹配的结果,适用于需要批量处理文本的场景。无论是从 HTML 中提取所有链接,还是从日志文件中筛选特定错误信息,PHP preg_match_all() 函数 都能高效完成任务。本文将通过循序渐进的方式,结合实例和比喻,帮助读者掌握这一函数的使用技巧。


三、函数基础解析:从单次匹配到多次匹配

1. 函数功能与基本语法

preg_match_all() 函数的作用是在字符串中全局搜索所有匹配正则表达式的内容,并返回匹配项的总数。其基本语法如下:

int preg_match_all ( string $pattern , string $subject , array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]] )  
  • $pattern:正则表达式模式,必须以分界符(如 /)包裹。
  • $subject:待匹配的原始字符串。
  • $matches:存储匹配结果的数组,结构由 $flags 参数决定。
  • $flags(可选):控制匹配结果的存储方式,默认为 PREG_PATTERN_ORDER
  • $offset(可选):指定搜索的起始位置,默认为 0(从开头开始)。

比喻理解
可以将 preg_match_all() 想象为一个“文本扫描仪”,它会在给定的字符串中逐行扫描,找到所有符合规则的“目标”,并将这些目标整理成列表返回。


2. 初级案例:提取网页中的邮箱地址

示例场景

假设有一个包含多封邮箱的字符串:

$text = "联系邮箱:support@example.com,客服邮箱:help@company.org,技术支持:team@service.net";  

实现代码

使用正则表达式匹配邮箱的通用模式:/[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}/。通过 preg_match_all() 提取所有匹配项:

$pattern = "/[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}/";  
preg_match_all($pattern, $text, $matches);  
print_r($matches[0]);  

输出结果

Array  
(  
    [0] => support@example.com  
    [1] => help@company.org  
    [2] => team@service.net  
)  

关键点

  • 正则表达式中 [\w.-]+ 匹配邮箱名部分(允许字母、数字、点、下划线和短横线)。
  • @ 是邮箱分隔符,必须原样匹配。
  • [\w.-]+\.[a-zA-Z]{2,} 匹配域名部分,确保至少有两个字母的顶级域名(如 .com)。

四、参数详解与注意事项

1. $matches 的存储结构

$matches 数组的结构由 $flags 参数决定,常见的两种模式为:

参数值描述
PREG_PATTERN_ORDER默认模式,将匹配的完整模式放在 $matches[0],子模式分组依次放在 $matches[1]$matches[2] 等。
PREG_SET_ORDER将每个匹配项的结果合并为子数组,例如 $matches[0] 包含第一个匹配的完整模式和子模式。

表格说明
通过表格对比两种模式的区别,帮助开发者根据需求选择存储方式。


2. $flags 参数的进阶用法

除了控制存储结构,$flags 还可以配合其他修饰符,例如:

  • PREG_OFFSET_CAPTURE:返回匹配项的偏移位置(即在字符串中的起始位置)。
  • PREG_UNMATCHED_AS_NULL:将未匹配的部分标记为 NULL(PHP 8.2+ 新增)。

示例代码

// 使用 PREG_OFFSET_CAPTURE  
preg_match_all($pattern, $text, $matches, PREG_OFFSET_CAPTURE);  
print_r($matches[0]);  

输出片段

Array  
(  
    [0] => Array  
        (  
            [0] => support@example.com  
            [1] => 11  // 匹配项在字符串中的起始位置  
        )  
    // 其他项类似  
)  

3. 注意事项

  • 正则表达式性能:复杂的模式可能导致执行缓慢,尤其在处理大文本时,需优化正则或考虑其他方法(如 DOMDocument 解析 HTML)。
  • 特殊字符转义:正则表达式中的特殊字符(如 .*$)需用反斜杠 \ 转义,或使用双引号包裹字符串。
  • 模式修饰符:通过在分界符后添加修饰符(如 /i 表示忽略大小写),可扩展匹配逻辑。

五、进阶技巧:复杂场景的实战应用

1. 提取带分组的结构化数据

假设需要从 HTML 表格中提取商品名称和价格:

<div class="product">  
    <span class="name">iPhone 15</span>  
    <span class="price">$999</span>  
</div>  
<div class="product">  
    <span class="name">Galaxy S24</span>  
    <span class="price">$899</span>  
</div>  

正则表达式设计

使用分组捕获名称和价格:

$pattern = "/<div class=\"product\">.*?<span class=\"name\">(.+?)<\/span>.*?<span class=\"price\">(.+?)<\/span>/is";  
  • .*? 表示非贪婪匹配任意字符,避免跨标签匹配。
  • (.+?) 分组捕获名称和价格,i 修饰符忽略大小写,s 修饰符允许 . 匹配换行符。

代码实现

preg_match_all($pattern, $html, $matches);  
foreach ($matches[1] as $index => $name) {  
    echo "商品名称:" . $name . ",价格:" . $matches[2][$index] . PHP_EOL;  
}  

输出结果

商品名称:iPhone 15,价格:$999  
商品名称:Galaxy S24,价格:$899  

2. 处理多行文本与模式修饰符

当处理多行日志时,可结合 m 修饰符(多行模式)和 ^/$ 来定位行首行尾:

$log = "2023-10-01 10:00:00 - ERROR: Database connection failed\n" .  
       "2023-10-01 10:01:00 - WARNING: Memory usage exceeded\n" .  
       "2023-10-01 10:02:00 - ERROR: File not found";  

// 提取所有错误信息  
$pattern = "/^.*?ERROR: (.*)/m";  
preg_match_all($pattern, $log, $matches);  
print_r($matches[1]);  // 输出所有错误描述部分  

六、常见问题与解决方案

1. 匹配结果为空的可能原因

  • 正则表达式错误:检查分组符号是否闭合,转义字符是否遗漏。
  • 模式修饰符缺失:例如未添加 s 修饰符导致无法匹配跨行内容。
  • 字符串编码问题:确保输入的 $subject 字符串无隐藏字符。

2. 性能优化建议

  • 简化模式:避免过度使用量词(如 .*),改用更精确的匹配规则。
  • 预编译模式:通过 preg_quote()preg_last_error() 检查模式合法性。
  • 替代方案:对于 HTML/XML 解析,优先使用 DOMDocumentSimpleXML

七、结论:掌握 preg_match_all() 的核心价值

通过本文的讲解,读者应能理解 PHP preg_match_all() 函数 的基本原理、参数配置和实际应用场景。无论是从文本中批量提取数据,还是解析结构化内容,该函数都能提供高效、灵活的解决方案。建议开发者在实践中逐步积累正则表达式的使用经验,并结合模式修饰符和分组技巧,应对更复杂的文本处理需求。

最后提醒:正则表达式的学习需要结合实践,建议读者通过在线工具(如 regex101.com)实时调试正则模式,逐步提升对 preg_match_all() 的掌控能力。


最新发布