本文目录导读:

在PHP中,正则分组提取内容主要使用 preg_match() 或 preg_match_all() 函数,以下是详细的使用方法:
基本分组提取
使用 preg_match() 提取单个匹配
$text = "我的手机号是:138-1234-5678";
$pattern = "/(\d{3})-(\d{4})-(\d{4})/";
if (preg_match($pattern, $text, $matches)) {
print_r($matches);
// 输出:
// Array (
// [0] => 138-1234-5678 // 完整匹配
// [1] => 138 // 第1个分组
// [2] => 1234 // 第2个分组
// [3] => 5678 // 第3个分组
// )
echo "区号:".$matches[1]; // 138
echo "中间:".$matches[2]; // 1234
echo "尾号:".$matches[3]; // 5678
}
使用 preg_match_all() 提取所有匹配
$text = "姓名:张三,年龄:25,城市:北京";
$pattern = "/([^:]+):([^,]+)/";
if (preg_match_all($pattern, $text, $matches)) {
print_r($matches);
// 输出:
// Array (
// [0] => Array ( [0] => 姓名:张三 [1] => 年龄:25 [2] => 城市:北京 )
// [1] => Array ( [0] => 姓名 [1] => 年龄 [2] => 城市 )
// [2] => Array ( [0] => 张三 [1] => 25 [2] => 北京 )
// )
// 提取所有姓名、年龄、城市
foreach ($matches[1] as $key => $label) {
echo "$label: " . $matches[2][$key] . "\n";
}
}
命名分组(推荐)
PHP 5.5.0+ 支持命名分组,使代码更易读:
$text = "2024-01-15";
$pattern = "/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/";
if (preg_match($pattern, $text, $matches)) {
// 通过名称访问
echo "年份:".$matches['year']; // 2024
echo "月份:".$matches['month']; // 01
echo "日期:".$matches['day']; // 15
// 也可以通过索引访问
echo $matches[0]; // 2024-01-15
echo $matches[1]; // 2024
echo $matches[2]; // 01
echo $matches[3]; // 15
}
实用示例
提取URL参数
$url = "http://example.com?id=123&name=test&page=1";
$pattern = "/(?:[?&])(?P<key>[^=]+)=(?P<value>[^&]+)/";
preg_match_all($pattern, $url, $matches, PREG_SET_ORDER);
$params = [];
foreach ($matches as $match) {
$params[$match['key']] = $match['value'];
}
print_r($params);
// Array ( [id] => 123 [name] => test [page] => 1 )
提取电子邮件信息
$email = "user@example.com";
$pattern = "/(?P<username>[^@]+)@(?P<domain>[^.]+)\.(?P<tld>\w+)/";
if (preg_match($pattern, $email, $matches)) {
echo "用户名:".$matches['username']; // user
echo "域名:".$matches['domain']; // example
echo "顶级域:".$matches['tld']; // com
}
替换中使用分组引用
$text = "2024-01-15";
// 将日期格式从 YYYY-MM-DD 改为 DD/MM/YYYY
$result = preg_replace(
"/(\d{4})-(\d{2})-(\d{2})/",
"$3/$2/$1", // 引用分组
$text
);
echo $result; // 15/01/2024
使用命名分组的替换
$text = "2024-01-15";
$result = preg_replace_callback(
"/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/",
function($matches) {
return "{$matches['day']}-{$matches['month']}-{$matches['year']}";
},
$text
);
echo $result; // 15-01-2024
高级技巧
非捕获分组 (?:...)
$text = "The quick brown fox";
$pattern = "/(?:The|A) (\w+) (\w+) fox/";
if (preg_match($pattern, $text, $matches)) {
print_r($matches);
// 注意:分组[1]仍然是第一个捕获组(quick),因为(?:)不捕获
// Array ( [0] => The quick brown fox [1] => quick [2] => brown )
}
贪婪与懒惰匹配
$text = "<div>内容1</div><div>内容2</div>"; // 贪婪匹配(会匹配尽可能多的内容) $pattern1 = "/<div>(.*)<\/div>/"; preg_match($pattern1, $text, $matches1); echo $matches1[1]; // 内容1</div><div>内容2 // 懒惰匹配(会匹配尽可能少的内容) $pattern2 = "/<div>(.*?)<\/div>/"; preg_match($pattern2, $text, $matches2); echo $matches2[1]; // 内容1
常见错误处理
// 错误:忘记转义特殊字符
$text = "价格是$99.99";
$pattern = "/价格是$(\d+)/"; // $会被解释为行尾
// 正确写法
$pattern = "/价格是\$(\d+)/";
// 错误:未检查匹配结果
if (preg_match($pattern, $text, $matches)) {
// 安全使用 $matches[1]
}
// 使用 PREG_OFFSET_CAPTURE 获取位置信息
preg_match("/(\w+)@(\w+)/", "user@example", $matches, PREG_OFFSET_CAPTURE);
print_r($matches);
// 每个匹配包含值和位置
总结建议
- 优先使用命名分组
(?P<name>...),使代码更易读 - 总是检查 preg_match() 返回值(是否成功匹配)
- 使用非捕获分组 当不需要捕获时
- 注意特殊字符转义,特别是 、、 等
- 对于大量文本,考虑使用 preg_match_all() 配合 PREG_SET_ORDER