сложный и рекурсивный regexp

Discussion in 'PHP' started by AnGeI, 29 Jul 2012.

  1. AnGeI

    AnGeI Elder - Старейшина

    Joined:
    8 Dec 2008
    Messages:
    396
    Likes Received:
    79
    Reputations:
    16
    Задача: построить регулярной выражение и посредством preg_replace сделать рекурсивные замены замены.
    Имеется текст, в котором встречается
    PHP:
    {вариант1|вариант2|вариант3}
    причем эти конструкции встречаются рекурсивно, т.е. может встречаться
    PHP:
    {вариант1|{вариант1_1|вариант1_2}|вариант3}}
    уровень вложенности от нуля до бесконечности.
    Регулярка должна обрабатывать этот текст и вместо {вариант1|вариант2|вариант3} => вариант1 (в идеале рандомный вариант, если такое возможно).

    Simple input:
    Code:
    В лесу родилась ёлочка {в лесу она {росла|жила|цвела}|в степу она росла}!
    Simple output:
    Code:
    1) В лесу родилась ёлочка в лесу она росла!
    2) В лесу родилась ёлочка в лесу она жила!
    3) В лесу родилась ёлочка в лесу она цвела!
    4) В лесу родилась элочка в степу она росла!
    
     
  2. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    AnGeI не желайте дурного и сделайте цикл + preg_replace_callback
     
    _________________________
  3. AnGeI

    AnGeI Elder - Старейшина

    Joined:
    8 Dec 2008
    Messages:
    396
    Likes Received:
    79
    Reputations:
    16
    Проблему рекурсии решает полностью )
    Подскажите тогда какую регулярку применить (без рекурсии).
    Code:
    _{v1|v2}_ => _v1_ или _v2_
    
    Не могу выцепить варианты, пробовал так:
    PHP:
    \{(((.*)|)+)(.*)\}
    Возле {} экранирование форум вырезал.
     
  4. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    AnGeI /showpost.php?p=2511238&postcount=17490 Для вложенных скобок присваивайте во временную переменную и снова подставляйте. Условие останова - 5 параметр функции http://php.net/preg_replace_callback
     
    _________________________
    #4 Gifts, 30 Jul 2012
    Last edited: 30 Jul 2012
  5. AnGeI

    AnGeI Elder - Старейшина

    Joined:
    8 Dec 2008
    Messages:
    396
    Likes Received:
    79
    Reputations:
    16
    Как раз то, что нужно.
    PHP:
    $text 'В лесу родилась ёлочка {в лесу она {росла|жила|цвела}|в степу она росла}!';

    function 
    cb($arr)
    {
        
    $arr explode('|'$arr[1]);
        return 
    $arr[array_rand($arr)];
    }
    $while min(substr_count($text'{'), substr_count($text'}'));
    for(
    $i=0$i $while$i++)
    $text preg_replace_callback('~\{([^{}]+)\}~','cb',$text);
    echo 
    $text;
    Но перебор вариантов (разбор условия) начинается с нижнего уровня, поэтому максимальное число раз обращение к рандомайзеру.
    Я так понял, чтобы избежать этого нужно текст делить на части, в каждой части имеется только одно глобальное условие и отдельно для каждой части так же обрабатывать и включить жадность в регулярке.
     
    #5 AnGeI, 30 Jul 2012
    Last edited: 30 Jul 2012
    1 person likes this.
  6. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    AnGeI Да, количество проходов по тексту равно максимальной вложенности фигурных скобок. Сделать рекурсивное регулярное выражение, которое справится со вложенными элементами - сложно и тормознуто. Я такое когда-то писал, могу поискать
     
    _________________________
    1 person likes this.
  7. AnGeI

    AnGeI Elder - Старейшина

    Joined:
    8 Dec 2008
    Messages:
    396
    Likes Received:
    79
    Reputations:
    16
    Gifts, интересно взглянуть.
     
  8. Kaimi

    Kaimi Well-Known Member

    Joined:
    23 Aug 2007
    Messages:
    1,732
    Likes Received:
    809
    Reputations:
    231
    http://stackoverflow.com/questions/133601/can-regular-expressions-be-used-to-match-nested-patterns
     
    _________________________
    1 person likes this.
  9. poldevel

    poldevel New Member

    Joined:
    11 Jun 2013
    Messages:
    1
    Likes Received:
    0
    Reputations:
    0
    PHP:
    /*   Парсинг сверху вниз с использованием рекурсивного 
          шаблона для выделения блоков верхнего уровня!   */

    function parseBlock($str){
      static 
    $pattern;

      
    $pattern '% (?<=^|[\|])                # начало блока данных
                (?:
                    [^\{\}\|]*           # возможный текст до скобок
                            ( (?P<opn_brt>\{)?         # блок в скобках верхнего уровня
                                                
                                           #  содержимое блока (в скобоках) верхнего уровня
                                 (
                                       (?P<opn_brt2>\{)? (?:[^\{\}]* | (?-3) ) (?(opn_brt2)\})
                       [^\{\}]*?
                             )*?
                          

                              (?P<clo_brt> (?(opn_brt) \} ) ) # конец: закр. скобка блока верхнего уровня
                        )?
                        [^\{\}\|]*     # возможный текст после скобок верхнего уровня
                )
                  %x'
    ;

      
    $rez '';

      
    preg_match_all($pattern$str$arrPREG_SET_ORDER);

      if(
    count($arr)==1){ // тескт без альтернатив
          
    if( !empty($arr[0][opn_brt]) ){ // если есть составной блок?
        
    preg_match('|^(.*?) \{ (.*) \} ([^\{\}]*) $|x'$str$arr); // содержимое внутри внешних фигурных скобок в $arr[2]
            
    $rez = ($arr[1]). parseBlock($arr[2]). ($arr[3]); // рез-т = текст перед скобками + рекурсия + текст после скобок
          
    }else{
            
    $rez $arr[0][0]; // составного блока и альтернатив нет - взять всю строку
          
    }
      }else{     
    // если есть альтернативы: count($arr)>1
         
    $k rand(0count($arr)-1);    // взять k - случайный индекс альтернативы
         
    $rez parseBlock($arr[$k][0]); // распарсить ее
      
    }
      return 
    $rez;
    }

    $text 'В лесу родилась ёлочка {в лесу она {росла|жила|цвела}|в степу она росла}!';

    echo 
    "Результат: "parseBlock($text);

     
    #9 poldevel, 12 Jun 2013
    Last edited: 13 Jun 2013