preg_match_all使用

本文深入解析PHP中的preg_match_all函数,通过实例讲解贪婪匹配与非贪婪匹配的区别,以及如何利用正则表达式进行字符串匹配与提取。

今天修改代码时读到别人写的一段代码preg_match_all的使用,顺便查阅了一下正则,下面谈谈这个函数的学习心得。

首先看下preg_match_all的定义

int preg_match_all ( string $pattern , string $subject , array $matchesint $flags = PREG_PATTERN_ORDER )

$pattern:要匹配的字符串,一般为正则表达式

$subject:源数据

$matches:匹配完之后的结果集,是array类型

$flag:PREG_PATTERN_ORDER和 PREG_SET_ORDER 稍后会介绍两种不同


例子1:

<?php
preg_match_all("|<[^>]+>(.*)</[^>]+>|U",
    "<b>example: </b><div align=left>this is a test</div>",
    $out, PREG_PATTERN_ORDER);
echo $out[0][0] . ", " . $out[0][1] . "\n";
echo $out[1][0] . ", " . $out[1][1] . "\n";
?>

结果:

<b>example: </b>, <div align=left>this is a test</div>
example: , this is a test
例子2:

<?php
preg_match_all("|<[^>]+>(.*)</[^>]+>|U",
    "<b>example: </b><div align=\"left\">this is a test</div>",
    $out, PREG_SET_ORDER);
echo $out[0][0] . ", " . $out[0][1] . "\n";
echo $out[1][0] . ", " . $out[1][1] . "\n";
?>
结果:

<b>example: </b>, example:
<div align="left">this is a test</div>, this is a test
通过这两个php官方手册例子可以了解这个函数的基本用法。


下面具体讲一下正则参数,首先知道: |U表示非贪婪匹配,即匹配一条数据之后就保存输入 详细请看正则标志的用法

string $pattern

例1:

"|<[^>]+>(.*)</[^>]+>|U"

首先匹配整个字符串,然后注意()中的部分,将匹配之后追加保存。 一般我们都是需要获取()中的值,可以通过返回的数组获取。

通过下面的例子我们来理解一下:

1:带有贪婪匹配的

preg_match_all("/today=(.*) /U", "today=2013-06-20 today=2013-06-21 ", $out, PREG_SET_ORDER);
var_dump($out);exit;
返回结果是:

array(2) {
  [0]=>
  array(2) {
    [0]=>
    string(17) "today=2013-06-20 "
    [1]=>
    string(10) "2013-06-20"
  }
  [1]=>
  array(2) {
    [0]=>
    string(17) "today=2013-06-21 "
    [1]=>
    string(10) "2013-06-21"
  }
}


2:不带有贪婪匹配的

preg_match_all("/today=(.*) /", "today=2013-06-20 today=2013-06-21 ", $out, PREG_SET_ORDER);
var_dump($out);exit;

array(1) {
  [0]=>
  array(2) {
    [0]=>
    string(34) "today=2013-06-20 today=2013-06-21 "
    [1]=>
    string(27) "2013-06-20 today=2013-06-21"
  }
}



<think>我们正在讨论`!preg_match()`的使用,这实际上是`preg_match()`的否定形式。在PHP中,`preg_match()`返回匹配的次数(0或1,因为它在第一次匹配后停止),所以`!preg_match()`通常用于检查是否没有匹配。 ### 一、`!preg_match()`的常见用法 在条件判断中,我们经常用`!preg_match()`来检查字符串中是否不存在某种模式。 #### 示例1:验证输入是否不包含特殊字符 ```php $username = $_POST['username']; // 要求用户名只包含字母和数字 if (!preg_match('/^[a-zA-Z0-9]+$/', $username)) { echo "用户名只能包含字母和数字!"; } ``` #### 示例2:检查是否不存在非法关键词 ```php $comment = $_POST['comment']; $forbiddenWords = array('spam', 'advertise', 'http://'); $pattern = '/'.implode('|', array_map('preg_quote', $forbiddenWords)).'/i'; if (!preg_match($pattern, $comment)) { // 没有匹配到禁用词,允许提交 save_comment($comment); } else { echo "评论包含禁用词!"; } ``` ### 二、常见问题与陷阱 1. **返回值判断错误** - 问题:混淆`preg_match()`返回的`0`(无匹配)和`false`(错误) - 错误示例: ```php // 错误:当正则表达式有语法错误时,preg_match返回false,!false为true,导致误判 if (!preg_match('/invalid regex(/', $input)) { // 这里可能因为正则错误而进入,并非真正的无匹配 } ``` - 正确做法:先检查错误 ```php $result = preg_match($pattern, $subject); if ($result === false) { // 处理正则错误 } elseif ($result === 0) { // 无匹配 } else { // 有匹配 } ``` 2. **多行匹配问题** - 问题:默认情况下,正则表达式将字符串视为单行,`^`和`$`只匹配整个字符串的开始和结束 - 解决方案:使用`m`修饰符启用多行模式 ```php // 检查字符串中是否没有以"end"开头的行 if (!preg_match('/^end/m', $text)) { echo "没有以'end'开头的行"; } ``` 3. **Unicode字符匹配** - 问题:未使用`u`修饰符导致中文字符等匹配失败 - 错误示例: ```php // 试图匹配中文字符,但未加u修饰符 if (!preg_match('/[\x{4e00}-\x{9fa5}]/', $text)) { // 可能错误地认为没有中文字符 } ``` - 正确做法: ```php if (!preg_match('/[\x{4e00}-\x{9fa5}]/u', $text)) { // 正确匹配Unicode } ``` 4. **性能问题** - 问题:复杂正则导致超时 - 优化:避免过度回溯,使用原子组、占有量词等 ```php // 低效:.*可能引起大量回溯 if (!preg_match('/<script>.*<\/script>/s', $html)) { ... } // 改进:使用非贪婪匹配 if (!preg_match('/<script>.*?<\/script>/s', $html)) { ... } ``` ### 三、最佳实践 1. **始终检查正则错误** 在关键逻辑中,检查`preg_match()`返回`false`的情况: ```php $pattern = '/[a-z'; if (($result = preg_match($pattern, $subject)) === false) { $error = preg_last_error_msg(); throw new Exception("正则错误: $error"); } ``` 2. **使用命名捕获组提高可读性**(PHP 7.0+) ```php if (!preg_match('/^(?<name>[a-z]+) (?<age>\d+)$/i', $input, $matches)) { echo "格式不匹配"; } else { // 即使匹配失败,$matches也可能有部分数据,但这里不会进入 } ``` 3. **调试技巧** 当`!preg_match()`表现不符合预期时: - 输出正则表达式和待匹配字符串进行验证 - 使用在线工具(如regex101.com)测试正则 - 开启`preg_last_error()`检查 ### 四、完整示例:表单验证 ```php function validate_email($email) { // 正则来自PHP官方文档 $pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'; return preg_match($pattern, $email) === 1; } $email = 'user@example.com'; if (!validate_email($email)) { echo "邮箱格式无效"; } else { echo "邮箱有效"; } ``` ### 五、总结 - `!preg_match()` 用于检测字符串中**不存在**正则模式匹配的内容 - 必须处理可能返回的`false`(正则错误) - 注意修饰符(如`s`, `m`, `u`)对匹配行为的影响 - 复杂正则需考虑性能问题 §§相关问题§§ 1. 如何用`preg_match_all`实现全局匹配? 2. `preg_replace`和`preg_filter`有什么区别? 3. 正则表达式中`(?=)`和`(?!)`的作用是什么? 4. 如何避免正则表达式中的回溯爆炸问题?</think>### PHP `!preg_match()` 用法详解与常见问题 `!preg_match()` 是 PHP 中**正则表达式匹配的否定判断**,常用于验证字符串**不符合**特定模式的情况。其核心逻辑是将 `preg_match()` 的返回值取反: - 当 `preg_match()` 返回 `0`(无匹配)时,`!preg_match()` 返回 `true` - 当 `preg_match()` 返回 `1`(有匹配)时,`!preg_match()` 返回 `false` #### 一、基本语法结构 ```php if (!preg_match($pattern, $subject)) { // 当字符串不匹配模式时执行的代码 } ``` #### 二、典型应用场景 1. **输入验证(黑名单模式)** ```php $username = "admin#"; // 验证用户名不含特殊字符 if (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) { echo "用户名只能包含字母、数字和下划线"; } ``` 2. **安全过滤(防注入攻击)** ```php $input = $_POST['search']; // 检测是否包含SQL注入特征 if (!preg_match('/\b(UNION|SELECT|INSERT\s+INTO|DROP\s+TABLE)\b/i', $input)) { // 安全处理逻辑 } ``` 3. **格式排除(拒绝特定格式)** ```php $filename = "backup.tar.gz"; // 拒绝压缩文件上传 if (!preg_match('/\.(zip|rar|gz|tar)$/i', $filename)) { // 允许上传非压缩文件 } ``` #### 三、常见问题与解决方案 1. **返回值误判问题** - **问题**:混淆 `0`(无匹配)和 `false`(错误) - **解决方案**:严格检查返回值类型 ```php $result = preg_match($pattern, $subject); if ($result === false) { // 正则错误处理 } elseif ($result === 0) { // 无匹配逻辑 } ``` 2. **正则表达式语法错误** - **问题**:无效正则导致返回 `false`,`!false` 为 `true` - **调试方法**: ```php if (@!preg_match('/invalid(regex/', $str)) { $error = preg_last_error(); error_log("正则错误: ".preg_last_error_msg()); } ``` 3. **多行文本处理异常** - **问题**:默认单行模式导致 `^` 和 `$` 行为不符合预期 - **解决方案**:使用 `m` 修饰符 ```php // 检测文本中不存在以"error:"开头的行 if (!preg_match('/^error:/m', $logContent)) { echo "日志无错误记录"; } ``` 4. **UTF-8 字符匹配问题** - **问题**:未使用 `u` 修饰符导致中文匹配失败 - **正确用法**: ```php // 验证不含中文 if (!preg_match('/[\x{4e00}-\x{9fa5}]/u', $text)) { echo "文本不含中文字符"; } ``` #### 四、性能优化技巧 1. **避免回溯爆炸** ```php // 低效写法(易引发回溯) !preg_match('/<div>.*<\/div>/s', $html); // 优化方案(使用非贪婪和明确边界) !preg_match('/<div>.*?<\/div>/s', $html); ``` 2. **设置回溯限制** ```php ini_set('pcre.backtrack_limit', 10000); // 防止超长文本匹配超时 ``` #### 五、完整示例:邮箱白名单验证 ```php $email = "user@example.com"; $allowedDomains = ['gmail.com', 'outlook.com', 'company.com']; $pattern = '/@('.implode('|', array_map('preg_quote', $allowedDomains)).')$/i'; if (!preg_match($pattern, $email)) { echo "仅允许以下邮箱: ".implode(', ', $allowedDomains); } else { echo "邮箱验证通过"; } ``` #### 六、调试建议 1. 使用在线工具(如 [regex101.com](https://regex101.com/))预先测试正则 2. 错误捕获: ```php set_error_handler(function($errno, $errstr) { if (str_contains($errstr, 'preg_match')) { die("正则错误: $errstr"); } }); ``` > **关键点**:`!preg_match()` 本质是 `preg_match() === 0` 的简写形式,但需注意区分 `0`(无匹配)和 `false`(错误)两种状态[^1][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值