导读:本期聚焦于乐少创作的《PHP如何用正则表达式提取双@包裹的动态令牌?》,敬请观看详情。模板引擎里经常会出现 @@token@@ 这种双@包裹的占位符,如何在PHP中准确地把它们全部提取出来并替换成动态内容?本文从正则表达式的基本写法入手,讲解 preg_match_all 的匹配原理、贪婪与非贪婪模式的差异,以及如何处理嵌套边界、空令牌和特殊字符等情况。文中还会给出一个可直接复用的令牌替换函数,并用性能对比说明 preg_replace_callback 相比多次 str_replace 的优势,帮助你写出更健壮的模板解析逻辑。

在做模板解析、消息推送占位符替换或者配置文件渲染时,经常会遇到形如 @@token@@ 的动态令牌。这类双字符边界的占位符看起来简单,但实际提取时却有不少细节容易踩坑,比如正则写得贪婪导致一次匹配吞掉多个令牌、令牌内容包含特殊字符时匹配失败、空令牌该不该匹配等。本文围绕PHP的PCRE函数,完整讲解如何稳定地从字符串中提取所有 @@token@@ 形式的令牌。

PHP如何用正则表达式提取双@包裹的动态令牌?

一、基础写法:用 preg_match_all 提取所有令牌

最直接的做法是使用 preg_match_all 配合捕获组。双@包裹的令牌可以用正则 /@@([^@]+)@@/ 来匹配,其中 ([^@]+) 是捕获组,表示令牌内容由一个或多个非@字符组成。匹配完成后,结果会存放在第三个参数引用的数组中,$matches[1] 就是所有令牌内容的列表。

$text = '欢迎 @@username@@,您的订单 @@order_id@@ 已发货,物流单号 @@tracking_no@@';

if (preg_match_all('/@@([^@]+)@@/', $text, $matches)) {
    // $matches[1] 中保存的就是去掉@@之后的内容
    print_r($matches[1]);
} else {
    echo '没有匹配到任何令牌';
}

上面的代码输出会是 ['username', 'order_id', 'tracking_no']。这里有个关键点:PCRE默认是回溯型引擎,遇到 @@a@@ 和 @@b@@ 连续出现的情况时,[^@]+ 这种排除型字符类会在第一个 @@ 处停下,不会越过边界去吞掉后面的令牌,这正是我们想要的效果。

如果写成 /@@(.+)@@/,就会出问题。点号加贪婪量词会一路匹配到字符串中最后一个 @@,导致一次匹配吞掉中间所有内容。虽然可以用非贪婪的 /@@(.+?)@@/ 缓解,但它在某些嵌套或紧邻的场景下仍可能出现歧义,所以排除型字符类 [^@]+ 才是最稳妥的选择。

二、处理边界情况:空令牌、特殊字符与命名捕获

实际业务中令牌内容往往不只是字母数字。假设令牌允许包含点号、下划线甚至中文,比如 @@user.name@@@@订单状态@@,前面的正则都能正常工作,因为它们不含@字符。但如果两个@@之间为空,写成 @@@@+ 量词不会匹配,这个行为通常符合预期。如果你确实希望空令牌也算合法,把 + 改成 * 即可,但要小心 @@@@ 本身也可能是四个分隔符连排导致的误判。

为了让代码可读性更好,推荐使用命名捕获组 (?<token>...),这样取结果时不用依赖数字下标:

$pattern = '/@@(?<token>[^@]+)@@/u';
// 结尾的u修饰符按UTF-8处理,中文令牌不会被截断

preg_match_all($pattern, $text, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
    echo '找到令牌: ' . $m['token'] . PHP_EOL;
}

注意两个细节:一是加了 PREG_SET_ORDER 标志后,每个完整匹配会作为一个子数组,遍历时语义更清晰;二是处理中文时务必加上 u 修饰符,否则 [^@]+ 在多字节字符上按字节匹配,可能把一个汉字从中间截断,得到乱码结果。

三、提取之后:用 preg_replace_callback 完成动态替换

提取令牌通常不是最终目的,把令牌映射成动态值才是。比起先提取再循环 str_replacepreg_replace_callback 一次遍历就能完成匹配和替换,效率更高,尤其是当模板很长、令牌很多的时候。下面是一个可以直接复用的工具函数:

function renderTokens(string $template, array $data): string
{
    return preg_replace_callback(
        '/@@([^@]+)@@/u',
        function ($m) use ($data) {
            $key = $m[1];
            // 令牌在数据中存在则替换,否则保留原文,方便排查
            return array_key_exists($key, $data) ? (string)$data[$key] : $m[0];
        },
        $template
    );
}

echo renderTokens(
    '亲爱的@@username@@,您购买的 @@product@@ 共计 @@amount@@ 元',
    ['username' => '张三', 'product' => '机械键盘', 'amount' => 399]
);
// 输出:亲爱的张三,您购买的 机械键盘 共计 399 元

这个实现有两个值得注意的设计:其一,未找到对应数据时保留原始令牌而不是替换成空字符串,这样调试模板时能一眼看出哪个变量缺失;其二,回调函数内用 array_key_exists 而不是 isset,因为变量值可能是 nullisset 会误判为不存在。

如果数据量非常大且同一个令牌在模板中重复出现很多次,还可以先在回调里做一层本地缓存,或者在正则中用 (?>...) 原子组减少回溯,进一步提升匹配性能。对绝大多数业务场景来说,preg_replace_callback 加排除型字符类的组合已经足够高效且不容易出错,可以作为模板占位符替换的标准方案。

PHP正则表达式 preg_match_all 动态令牌提取修改时间:2026-09-13 11:42:31

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260913/55978.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。