За последние 24 часа нас посетил 17571 программист и 1314 роботов. Сейчас ищут 1458 программистов ...

Не понятное поведение preg_match.

Тема в разделе "Регулярные выражения", создана пользователем denis_vl, 9 июн 2016.

Метки:
  1. denis_vl

    denis_vl Новичок

    С нами с:
    9 июн 2016
    Сообщения:
    3
    Симпатии:
    0
    Доброго времени суток,

    Сразу приведу кусок кода:
    PHP:
    1. text = <<<TEXT
    2. <megablock>
    3.   <block id=block1>Block 1 is permanent</block>
    4.   <block id=block2>Block 2 is permanent</block>
    5.   <block id=block3>Block 3 is not permanent</block>
    6.   <block id=block4>Block 4 is permanent</block>
    7. </megablock>
    8. TEXT;
    9.  
    10. $regex =
    11.             '\<megablock\>.*?'.
    12.             '\<block\s+id=block1>(?<block1_content>.*?)\<\/block\>.*?'.
    13.             '\<block\s+id=block2>(?<block2_content>.*?)\<\/block\>.*?'.
    14.             '(?:\<block\s+id=block3>(?<block3_content>.*?)\<\/block\>)?.*?'.
    15.             '\<block\s+id=block4>(?<block4_content>.*?)\<\/block\>.*?'.
    16.             '\<\/megablock\>';
    17.  
    18. if(preg_match('/'.$regex.'/s', $text, $matches)) {
    19.     print_r($matches);
    20. }
    Проблема возникает со строкой
    PHP:
    1. '(?:\<block\s+id=block3>(?<block3_content>.*?)\<\/block\>)?.*?'.
    Если я все правильно понимаю, то вопросительный знак после блока в скобках говорит о том, что этот блок будет появляться 0 или 1 раз. тоже самое можно написать {0,1}. Так и есть на самом деле.
    Но в результате block3_content отсутствует:
    Код (Text):
    1. Array
    2. (
    3.     [0] => <megablock>
    4.   <block id=block1>Block 1 is permanent</block>
    5.   <block id=block2>Block 2 is permanent</block>
    6.   <block id=block3>Block 3 is not permanent</block>
    7.   <block id=block4>Block 4 is permanent</block>
    8. </megablock>
    9.     [block1_content] => Block 1 is permanent
    10.     [block2_content] => Block 2 is permanent
    11.     [block3_content] =>
    12.     [block4_content] => Block 4 is permanent
    13. )
    Если же я уберу вопросительный знак после скобок, или поставлю там одно из: {1}, {1,}, +, то preg_match находит этот блок.
    Код (Text):
    1.  
    2. Array
    3. (
    4.     [0] => <megablock>
    5.   <block id=block1>Block 1 is permanent</block>
    6.   <block id=block2>Block 2 is permanent</block>
    7.   <block id=block3>Block 3 is not permanent</block>
    8.   <block id=block4>Block 4 is permanent</block>
    9. </megablock>
    10.     [block1_content] => Block 1 is permanent
    11.     [block2_content] => Block 2 is permanent
    12.     [block3_content] => Block 3 is not permanent
    13.     [block4_content] => Block 4 is permanent
    14. )
    Может кто подскажет, почему не появляется блок 3 в первом случае?

    PS: блок 3 так же не появляется, если после скобок поставить *
     
  2. VLK

    VLK Старожил

    С нами с:
    15 дек 2013
    Сообщения:
    3.010
    Симпатии:
    58
    может как то стоит по иному это делать, а не пытаться в регулярное выражение засунуть весь блок?
     
  3. denis_vl

    denis_vl Новичок

    С нами с:
    9 июн 2016
    Сообщения:
    3
    Симпатии:
    0
    То, что я тут привел, это только лишь пример. В реальности все чуть сложнее. Поэтому надо делать именно так.
    Да и вопрос в другом, почему отрабатывает не так, как ожидается? Где ошибка?
     
  4. VLK

    VLK Старожил

    С нами с:
    15 дек 2013
    Сообщения:
    3.010
    Симпатии:
    58
    не знаю чувак, у тебя все слишком мудрено, по этому жди профессионалов в области регулярных выражений, или упрощай, например можно в 2 этапа это делать, сначала вытаскивать блок megablock а потом уже с просто block работать, как говориться выход есть всегда, после чего он достал пистолет и выстрелил себе в голову.
     
  5. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Может через Document Object Model (DOM) сделать + xpath или css selectors? Тебе нужно проверку на правильную структуру сделать или вытащить значения?
     
    metadon нравится это.
  6. denis_vl

    denis_vl Новичок

    С нами с:
    9 июн 2016
    Сообщения:
    3
    Симпатии:
    0
    Да вопрос не в методах. Я уже задачу решил другим методом. Просто интересно, почему именно в этом случае так работает.
     
  7. rodent90

    rodent90 Новичок

    С нами с:
    26 мар 2015
    Сообщения:
    533
    Симпатии:
    37
    Потому, что регулярное выражение не соответствует задумке ;), а способ которым решил соответствует.
     
    denis01 нравится это.
  8. Emilien

    Emilien Активный пользователь

    С нами с:
    30 июн 2016
    Сообщения:
    246
    Симпатии:
    156
    Проблема возникает из-за применения .*? для захвата пространства между концом одного блока и началом следующего </block>.*?<block
    Решит проблему использование \s* между блоками.
    PHP:
    1. $regex =
    2.             '\<megablock\>\s*'.
    3.             '\<block\s+id=block1>(?<block1_content>.*?)\<\/block\>\s*'.
    4.             '\<block\s+id=block2>(?<block2_content>.*?)\<\/block\>\s*'.
    5.             '(?:\<block\s+id=block3>(?<block3_content>.*?)\<\/block\>)?\s*'.
    6.             '\<block\s+id=block4>(?<block4_content>.*?)\<\/block\>\s*'.
    7.             '\<\/megablock\>';
    Еще можно организовать поиск с применением preg_match_all и метасимвола \G.
    PHP:
    1. $text = <<<TEXT
    2. <megablock>
    3.   <block id=block1>Block 1 is permanent</block>
    4.   <block id=block2>Block 2 is permanent</block>
    5.   <block id=block3>Block 3 is not permanent</block>
    6.   <block id=block4>Block 4 is permanent</block>
    7. </megablock>
    8. TEXT;
    9.  
    10. $regex = <<<RE
    11. @
    12. (?: <megablock> | \G(?!\A) )
    13. \s*
    14. <block\s+id=([^>]+)>(.*?)</block>
    15. @xs
    16. RE;
    17.  
    18. if(preg_match_all($regex, $text, $matches, PREG_SET_ORDER)) {
    19.     $result = array();
    20.     foreach ($matches as $m) {
    21.         $result[$m[1]] = $m[2];
    22.     }
    23.     print_r($result);
    24. }