PHP 正则表达式(PCRE)(长文讲解)

PHP 正则表达式(PCRE):从入门到实战应用

在处理文本数据时,你是否经常遇到需要从一段字符串中提取特定信息的场景?比如验证邮箱格式、过滤敏感词、解析日志文件,或是从网页中抓取标题?这些任务如果用传统的字符串匹配方法,代码会变得冗长且难以维护。而 PHP 正则表达式(PCRE) 正是为这类问题量身打造的利器。

它不是简单地“查找”字符串,而是通过定义一套规则(模式),让程序“理解”你想找的内容。你可以把它想象成一个智能搜索过滤器,不仅能找“有没有”,还能告诉你“是什么”“在哪”“有多少”。

本文将带你系统掌握 PHP 正则表达式(PCRE) 的核心语法与实战技巧,无论你是刚接触 PHP 的新手,还是有一定经验的开发者,都能从中获得实用价值。


什么是 PHP 正则表达式(PCRE)

PCRE 是“Perl Compatible Regular Expressions”的缩写,意为“与 Perl 兼容的正则表达式”。PHP 从 5.3 版本起,内置了 PCRE 扩展,成为处理复杂文本匹配的首选方案。

与传统字符串函数(如 strpossubstr)相比,正则表达式更强大、更灵活。它允许你用“模式”描述文本结构,从而实现精准匹配。

举个例子:

<?php
$text = "我的邮箱是 john@example.com,电话是 138-1234-5678。";

// 用正则匹配邮箱格式
if (preg_match('/^\w+@\w+\.\w+$/', $text, $matches)) {
    echo "找到了邮箱:{$matches[0]}\n"; // 输出:找到了邮箱:john@example.com
}

这段代码中,/^\w+@\w+\.\w+$/ 就是一个正则表达式模式。它描述了“以字母数字下划线开头,中间有 @,后面是域名和顶级域名”的邮箱结构。

注释:preg_match 是 PHP 中用于执行正则匹配的核心函数。/.../ 是模式的分隔符,^ 表示字符串开头,\w 匹配字母、数字、下划线,@ 是字面量字符,\. 表示点号本身(因为点号在正则中有特殊含义),$ 表示字符串结尾。


常用的元字符与量词

正则表达式的核心在于“元字符”——这些字符本身没有字面意义,而是赋予特殊含义。理解它们是掌握 PCRE 的第一步。

常见元字符

元字符 含义 示例
. 匹配任意单个字符(换行符除外) a.c 可匹配 "abc"、"a1c"
^ 匹配字符串开头 ^Hello 匹配以 Hello 开头的字符串
$ 匹配字符串结尾 world$ 匹配以 world 结尾的字符串
* 匹配前一个字符 0 次或多次 a* 可匹配 ""、"a"、"aa"
+ 匹配前一个字符 1 次或多次 a+ 不能匹配 "",但能匹配 "a"、"aa"
? 匹配前一个字符 0 次或 1 次 a? 可匹配 "" 或 "a"
\d 匹配数字 0-9 \d{3} 匹配三个连续数字
\w 匹配字母、数字、下划线 \w+ 匹配一个或多个单词字符
\s 匹配空白字符(空格、制表符、换行符) \s+ 匹配一个或多个空白

量词使用示例

<?php
$phone = "电话:138-1234-5678";

// 匹配 3 位数字 + 连字符 + 4 位数字 + 连字符 + 4 位数字
if (preg_match('/\d{3}-\d{4}-\d{4}/', $phone, $matches)) {
    echo "找到有效手机号:{$matches[0]}\n"; // 输出:找到有效手机号:138-1234-5678
}

注释:\d{3} 表示匹配恰好 3 个数字,{4} 同理。这种 {n} 写法是量词的简写形式,可以精确控制重复次数。


字符类与分组

当你要匹配的字符范围较广时,字符类(Character Classes)就派上用场了。

字符类

写法 含义
[abc] 匹配 a、b 或 c 中的任意一个
[a-z] 匹配 a 到 z 之间的任意小写字母
[0-9] 匹配 0 到 9 之间的任意数字
[^abc] 匹配非 a、非 b、非 c 的字符(取反)

分组与捕获

分组用括号 ( ) 表示,它不仅能将多个字符组合成一个单元,还能“记住”匹配的内容,供后续使用。

<?php
$text = "姓名:张三,年龄:25岁,城市:北京";

// 匹配姓名、年龄、城市三部分
if (preg_match('/姓名:(.*?)[,,]年龄:(\d+)岁[,,]城市:(.*?)$/', $text, $matches)) {
    echo "姓名:{$matches[1]}\n";     // 张三
    echo "年龄:{$matches[2]}\n";     // 25
    echo "城市:{$matches[3]}\n";     // 北京
}

注释:(...) 是捕获组,每个组的内容会被存入 $matches 数组中。.*? 是非贪婪匹配,避免一次性吃掉所有内容。[,,] 表示中文逗号或英文逗号,确保兼容多种分隔符。


常用 PHP 正则函数解析

PHP 提供了多个函数来支持正则操作,掌握它们能让你高效完成任务。

preg_match:匹配一次

用于判断是否匹配成功,返回 1(成功)、0(失败)或 false(错误)。

<?php
$email = "user@domain.com";

if (preg_match('/^\w+@\w+\.\w+$/', $email)) {
    echo "邮箱格式正确\n";
} else {
    echo "邮箱格式错误\n";
}

preg_match_all:全局匹配

找出所有匹配项,适合提取多个结果。

<?php
$text = "电话:138-1234-5678,另一个:159-8765-4321";

// 提取所有手机号
preg_match_all('/\d{3}-\d{4}-\d{4}/', $text, $matches);

foreach ($matches[0] as $phone) {
    echo "手机号:{$phone}\n";
}

preg_replace:替换匹配内容

用新字符串替换匹配到的部分。

<?php
$html = "<p>欢迎访问我们的网站</p><a href='/about'>关于我们</a>";

// 将所有 a 标签替换为链接文本
$result = preg_replace('/<a\s+[^>]*>(.*?)<\/a>/', '[$1]', $html);
echo $result; // 输出:[欢迎访问我们的网站][关于我们]

注释:<a\s+[^>]*> 匹配 a 标签及其属性,[^>]* 表示非 > 的任意字符,\s+ 表示一个或多个空白符。


实战案例:邮箱与手机号验证

我们来做一个完整的验证功能,展示 PHP 正则表达式(PCRE) 如何在实际项目中应用。

<?php
function validateEmail($email) {
    // 常见邮箱格式:用户名@域名.后缀
    $pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
    return preg_match($pattern, $email);
}

function validatePhone($phone) {
    // 支持 11 位手机号,可带连字符或空格
    $pattern = '/^1[3-9]\d{9}$|^\d{3}[-\s]?\d{4}[-\s]?\d{4}$/';
    return preg_match($pattern, $phone);
}

// 测试数据
$testEmails = [
    'user@example.com',
    'invalid.email',
    'test@sub.domain.co.uk'
];

$testPhones = [
    '13812345678',
    '138-1234-5678',
    '138 1234 5678',
    '12345678901'
];

echo "=== 邮箱验证 ===\n";
foreach ($testEmails as $email) {
    echo "{$email} -> " . (validateEmail($email) ? '有效' : '无效') . "\n";
}

echo "\n=== 手机号验证 ===\n";
foreach ($testPhones as $phone) {
    echo "{$phone} -> " . (validatePhone($phone) ? '有效' : '无效') . "\n";
}

注释:[a-zA-Z0-9._%+-]+ 表示邮箱用户名部分可包含字母、数字、点、下划线、百分号、加号、减号。{2,} 表示至少两个字符,确保顶级域名如 .com、.org 有效。^1[3-9]\d{9}$ 是中国大陆手机号的标准格式。


常见陷阱与最佳实践

在使用 PHP 正则表达式(PCRE) 时,有几个容易踩坑的地方需要特别注意:

  1. 忘记转义特殊字符:如 .*+? 等在正则中有特殊含义,若要匹配字面量,必须加反斜杠 \.
  2. 贪婪 vs 非贪婪:默认 *+ 是贪婪的,会尽可能多地匹配。如需最小匹配,使用 *?+?
  3. 性能问题:过于复杂的正则可能导致性能下降,尤其在处理大文本时。
  4. 编码问题:确保文本是 UTF-8 编码,否则中文字符可能匹配失败。可配合 u 修饰符使用:/pattern/u
<?php
// 正确:匹配中文字符
$text = "你好,世界";
if (preg_match('/^[\p{Han}]+$/u', $text)) {
    echo "包含中文字符\n";
}

注释:\p{Han} 是 Unicode 中文字符类别,u 修饰符启用 UTF-8 模式,确保中文能被正确识别。


总结

PHP 正则表达式(PCRE) 是处理文本的强大工具,它不仅能简化字符串操作,还能提升代码的可读性和维护性。通过掌握元字符、量词、分组、字符类等核心概念,你可以在项目中灵活应对各种文本处理需求。

从邮箱验证到日志解析,从数据清洗到内容提取,正则表达式都能成为你的得力助手。虽然初学时可能觉得复杂,但只要多写多练,你会发现它其实非常“有逻辑”且“有美感”。

别再用繁琐的字符串处理了,让 PHP 正则表达式(PCRE) 帮你写出更优雅、更高效的代码。