Доброго времени суток, Сразу приведу кусок кода: PHP: text = <<<TEXT <megablock> <block id=block1>Block 1 is permanent</block> <block id=block2>Block 2 is permanent</block> <block id=block3>Block 3 is not permanent</block> <block id=block4>Block 4 is permanent</block> </megablock> TEXT; $regex = '\<megablock\>.*?'. '\<block\s+id=block1>(?<block1_content>.*?)\<\/block\>.*?'. '\<block\s+id=block2>(?<block2_content>.*?)\<\/block\>.*?'. '(?:\<block\s+id=block3>(?<block3_content>.*?)\<\/block\>)?.*?'. '\<block\s+id=block4>(?<block4_content>.*?)\<\/block\>.*?'. '\<\/megablock\>'; if(preg_match('/'.$regex.'/s', $text, $matches)) { print_r($matches); } Проблема возникает со строкой PHP: '(?:\<block\s+id=block3>(?<block3_content>.*?)\<\/block\>)?.*?'. Если я все правильно понимаю, то вопросительный знак после блока в скобках говорит о том, что этот блок будет появляться 0 или 1 раз. тоже самое можно написать {0,1}. Так и есть на самом деле. Но в результате block3_content отсутствует: Код (Text): Array ( [0] => <megablock> <block id=block1>Block 1 is permanent</block> <block id=block2>Block 2 is permanent</block> <block id=block3>Block 3 is not permanent</block> <block id=block4>Block 4 is permanent</block> </megablock> [block1_content] => Block 1 is permanent [block2_content] => Block 2 is permanent [block3_content] => [block4_content] => Block 4 is permanent ) Если же я уберу вопросительный знак после скобок, или поставлю там одно из: {1}, {1,}, +, то preg_match находит этот блок. Код (Text): Array ( [0] => <megablock> <block id=block1>Block 1 is permanent</block> <block id=block2>Block 2 is permanent</block> <block id=block3>Block 3 is not permanent</block> <block id=block4>Block 4 is permanent</block> </megablock> [block1_content] => Block 1 is permanent [block2_content] => Block 2 is permanent [block3_content] => Block 3 is not permanent [block4_content] => Block 4 is permanent ) Может кто подскажет, почему не появляется блок 3 в первом случае? PS: блок 3 так же не появляется, если после скобок поставить *
То, что я тут привел, это только лишь пример. В реальности все чуть сложнее. Поэтому надо делать именно так. Да и вопрос в другом, почему отрабатывает не так, как ожидается? Где ошибка?
не знаю чувак, у тебя все слишком мудрено, по этому жди профессионалов в области регулярных выражений, или упрощай, например можно в 2 этапа это делать, сначала вытаскивать блок megablock а потом уже с просто block работать, как говориться выход есть всегда, после чего он достал пистолет и выстрелил себе в голову.
Может через Document Object Model (DOM) сделать + xpath или css selectors? Тебе нужно проверку на правильную структуру сделать или вытащить значения?
Да вопрос не в методах. Я уже задачу решил другим методом. Просто интересно, почему именно в этом случае так работает.
Проблема возникает из-за применения .*? для захвата пространства между концом одного блока и началом следующего </block>.*?<block Решит проблему использование \s* между блоками. PHP: $regex = '\<megablock\>\s*'. '\<block\s+id=block1>(?<block1_content>.*?)\<\/block\>\s*'. '\<block\s+id=block2>(?<block2_content>.*?)\<\/block\>\s*'. '(?:\<block\s+id=block3>(?<block3_content>.*?)\<\/block\>)?\s*'. '\<block\s+id=block4>(?<block4_content>.*?)\<\/block\>\s*'. '\<\/megablock\>'; Еще можно организовать поиск с применением preg_match_all и метасимвола \G. PHP: $text = <<<TEXT <megablock> <block id=block1>Block 1 is permanent</block> <block id=block2>Block 2 is permanent</block> <block id=block3>Block 3 is not permanent</block> <block id=block4>Block 4 is permanent</block> </megablock> TEXT; $regex = <<<RE @ (?: <megablock> | \G(?!\A) ) \s* <block\s+id=([^>]+)>(.*?)</block> @xs RE; if(preg_match_all($regex, $text, $matches, PREG_SET_ORDER)) { $result = array(); foreach ($matches as $m) { $result[$m[1]] = $m[2]; } print_r($result); }