2012-10-10 109 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(),甚至不用使用正則表達式。 –