复杂的递归正则表达式模式

时间:2013-11-15 18:18:31

标签: regex recursion conditional-statements

我正在尝试在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).))匹配(直到最后))。

帮助我,我被困住了。

2 个答案:

答案 0 :(得分:1)

尝试以下模式:

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

Regex101 Demo

编辑:确定尝试将模式更改为以下模式:

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

如果这也不起作用,请尝试将.+更改为.+?。如果所有这些对您不起作用,那么您可能(我认为这是更好的解决方案)需要使用解析器而不是正则表达式。

Regex101 Demo 2

答案 1 :(得分: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;
        }
    }
}