2013-11-15 41 views
1

我想在php模板系統中做條件語句,但是我在使它工作時遇到了一些問題。複雜的遞歸正則表達式模式

我的語法是(condition? value to show if condition is true)。使用這種模式很容易實現匹配:\((\w+)\?(.+?)\)。問題是我需要它遞歸地工作。

我想這些模式的字符串(it should be (a?working(b? with nested conditions).))

\((\w+)\?(.+?|(?R))\)但它匹配(a?working(b? with nested conditions)(最終跳過.)

\((\w+)\?(.+|(?R))\)(a?working(b? with nested conditions).))(一切,直到最後))相匹配。

幫幫我,我卡住了。

+0

我需要捕獲它們。另外你的模式不允許在值組中使用'()'字符,這不是我想要的。 – Arrvi

回答

1

請嘗試以下方式:

\((\w+)\?([^()]|\((?!\w+\?)|(?R))+\) 

Regex101 Demo

編輯: OK儘量變着花樣下列之一:

\((\w+)\?(.+|\((?!\w+\?)|(?R))+\) 
      ^^ 

如果這不起作用,以及,請嘗試更改.+.+?。如果所有這些都不適合你,那麼你可能(我認爲這是更好的解決方案)需要使用解析器而不是正則表達式。

Regex101 Demo 2

+0

差不多。我忘了提及值組可以包含'()'字符。抱歉。這並不能簡化任務,是嗎? – Arrvi

+0

@Arrvi我編輯了我的答案,請檢查編輯。 –

+0

沒有。這根本不起作用。正如你所說 - 我可能會爲它寫一個解析器。不管怎麼說,還是要謝謝你。 – Arrvi

0

正如@Sniffer說,我做了一個解析器。這很雜亂,但是做這個工作。它是模板系統類的一部分。 $this->rendered是一個正在被解析的字符串。

const COND_START = '(('; 
const COND_END = '))'; 
const COND_SEP = '?'; 
const COND_NOT = '!'; 
private function parseConditionals() 
{ 
    for (
     $i=0, 
     $level=0, 
     $levelstart=array(0=>0), 
     $levelseparator=array(0=>0), 
     $levelname=array(0=>'__main__'); 
     $i < strlen($this->rendered); 
    ) { 
     $startpos = strpos($this->rendered, self::COND_START, $i); 
     $seppos = strpos($this->rendered, self::COND_SEP, $i); 
     $endpos = strpos($this->rendered, self::COND_END, $i); 

     if (($startpos === FALSE) && ($endpos === FALSE)) { 
      $i = strlen($this->rendered); 
     } elseif (($startpos !== FALSE) && $startpos < $endpos) { 
      if ($seppos < $endpos) { 
       $level++; 
       $levelstart[$level] = $startpos; 
       $levelseparator[$level] = $seppos; 
       $levelname[$level] = substr(
        $this->rendered, 
        $startpos+strlen(self::COND_START), 
        $seppos-$startpos-strlen(self::COND_START) 
       ); 
       $i = $seppos + strlen(self::COND_SEP); 
      } else { 
       $i = $startpos + strlen(self::COND_START); 
      } 
     } else { 
      $originallen = strlen($this->rendered); 
      if ($level > 0) { 
       $not = false; 
       if (strpos($levelname[$level], self::COND_NOT) === 0) { 
        $levelname[$level] = substr($levelname[$level], strlen(self::COND_SEP)); 
        $not = true; 
       } 
       if ( 
        !$this->get($levelname[$level]) == $not 
       ) { 
        $this->rendered = substr_replace(
         $this->rendered, 
         '', 
         $endpos, 
         strlen(self::COND_END) 
        ); 
        $this->rendered = substr_replace(
         $this->rendered, 
         '', 
         $levelstart[$level], 
         $levelseparator[$level]-$levelstart[$level]+strlen(self::COND_SEP) 
        ); 
       } else { 
        $this->rendered = substr_replace(
         $this->rendered, 
         '', 
         $levelstart[$level], 
         $endpos-$levelstart[$level]+strlen(self::COND_END) 
        ); 
       } 
       $level--; 

      } 
      $i = $endpos + strlen(self::COND_END); 
      $i += strlen($this->rendered)-$originallen; 
     } 
    } 
}