2012-10-10 110 views
1

尝试替换一个字符串,但它似乎只匹配第一个匹配项,并且如果我有另一个匹配项,它不匹配任何内容,所以我认为我需要添加某种类型的结尾分隔符?PHP - preg_replace不匹配多个匹配项

我的代码:

$mappings = array(
    'fname'  => $prospect->forename, 
    'lname'  => $prospect->surname, 
    'cname'  => $prospect->company, 
); 

foreach($mappings as $key => $mapping) if(empty($mapping)) $mappings[$key] = '$2'; 

$match = '~{(.*)}(.*?){/.*}$~ise'; 
$source  = 'Hello {fname}Default{/fname} {lname}Last{/lname}'; 
// $source = 'Hello {fname}Default{/fname}'; 

$text = preg_replace($match, '$mappings["$1"]', $source); 

所以,如果我用的是真实评价的$源,它匹配得很好,如果我的代码目前使用上述那里的2场比赛的一个,它不匹配任何东西我得到一个错误:

Message: Undefined index: fname}Default{/fname} {lname 

Filename: schedule.php(62) : regexp code 

所以我说得对,我需要提供一个结束分隔符或什么?

感谢, 基督教

+1

您正在使用'。*?',但留下其他匹配''*'贪婪。 (提示:如果有明显的字符允许或禁止,总是限制模式。) – mario

+0

谢谢马里奥 - 错过了一个。关于主要问题的任何想法? :) –

+1

个人而言,我会使用['{([^}] +)}(。*?){/ \ 1}'](http://rubular.com/r/6HCgvUKIB7)。 – NullUserException

回答

1

显然你的正则表达式匹配fname}Default{/fname} {lname,而不是Default

正如我所提到的here使用{(.*?)}而不是{(.*)}

{在正则表达式中有特殊含义,所以您应该将其转义\\{

我建议使用preg_replace_callback而不是e修饰符(您有更多的流控制和语法高亮,并且不可能强制您的程序执行恶意代码)。

你所犯的最后一个错误是不检查请求的索引是否存在。 :)

我的解决办法是:

<?php 

class A { // Of course with better class name :) 
    public $mappings = array(
     'fname' => 'Tested' 
    ); 

    public function callback($match) 
    { 
     if(isset($this->mappings[$match[1]])){ 
      return $this->mappings[$match[1]]; 
     } 

     return $match[2]; 
    } 
} 

$a = new A(); 
$match = '~\\{([^}]+)\\}(.*?)\\{/\\1\\}~is'; 
$source  = 'Hello {fname}Default{/fname} {lname}Last{/lname}'; 

echo preg_replace_callback($match, array($a, 'callback'), $source); 

这导致到:

[[email protected] tmp]$ php stack.php 
Hello Tested Last 
+0

你还必须做最后一个'。*'不要贪婪并删除字符串锚点'$'的结尾以使其正常工作。另外,这里没有必要转义'{'。 – NullUserException

+0

辉煌 - 谢谢你的帮助,工作:) - 我已经去了@NullUserException的表达式,但你的实现。再次感谢! –

+0

@ christian.thomas这是一样的表达,除了这个到处都有逃脱。有些人喜欢玩它安全,逃避一切;为了获得更好的可读性,我宁愿尽可能少地转义。 – NullUserException

1

你的正则表达式锚定到字符串的末尾,以便在关闭{/whatever}必须是最后一次你的字符串中的东西。另外,由于您的开启和关闭标签只是.*,因此没有任何内容可以确保它们匹配。你想要的是确保你的结束标签符合你的开始标签 - 使用反向引用,如{(.+)}(.*?){/\1}将确保它们是相同的。我确信还有其他的陷阱 - 如果你能够控制你正在使用的字符串格式(IE - 你正在滚动你自己的模板语言),我会认真考虑转向更简单的,更容易匹配格式。由于您不是“保存”默认值,因此使用封闭标签不会增加附加值,但会使分析更加复杂。只需使用$VARNAME就可以很好地匹配(\$[A-Z]+),而不涉及反向引用或不得不明确说明您正在使用非贪婪匹配。

+0

谢谢肖恩。仍然有很多学习与正则表达式:) –

+0

@ christian.thomas正则表达式是一个惊人的强大的工具。了解它们最重要的是**不使用**时 - 使用更简单的标签语法可以让用户只使用str_replace(),甚至不用使用正则表达式。 –