Regex для соответствия ограничителей верхнего уровня в многомерной строке

У меня есть файл, который структурирован в большой многомерной структуре, похожей на json, но недостаточно закрытой для использования библиотеки json.

Данные выглядят примерно так:

alpha { beta { charlie; } delta; } echo; foxtrot { golf; hotel; } 

Регулярное выражение, которое я пытаюсь построить (для preg_match_all), должно совпадать с каждым родителем верхнего уровня (с разделителями {}), чтобы я мог рекурсивно проходить через совпадения, создавая многомерный php-массив, который представляет данные.

Первое regex, которое я пробовал, это /(?<=\{).*(?=\})/s который жадно соответствует содержимому внутри фигурных скобок, однако это не совсем так, как если на верхнем уровне более одного брата матч слишком жадный. Пример:

Использование regex /(?<=\{).*(?=\})/s match дается как:

Матч 1:

  beta { charlie; } delta; } echo; foxtrot { golf; hotel; 

Вместо этого результат должен быть: Матч 1:

  beta { charlie; } delta; 

Матч 2:

  golf; hotel; 

Итак, мастера регулярных выражений, какую функцию я пропускаю здесь или мне нужно решить это с помощью php? Любые советы очень приветствуются 🙂

Вы не можете делать это с помощью регулярных выражений.

Кроме того, если вы хотите сопоставить глубокие-мелкие блоки, вы можете использовать \{[^\{\}]*?\} И preg_replace_callback() чтобы сохранить это значение, и вернуть значение null чтобы удалить его из строки. Обратный вызов должен будет позаботиться о вложении значения соответствующим образом.

 $heirarchalStorage = ...; do { $string = \preg_replace_callback('#\{[^\{\}]*?\}#', function($block) use(&$heirarchalStorage) { // do your magic with $heirarchalStorage // in here return null; }, $string); } while (!empty($string)); 

Неполные, не проверенные и не гарантированные.

Этот подход требует, чтобы строка была завершена в {} , иначе финальное совпадение не произойдет, и вы будете навечно зацикливаться.

Это очень много ( неэффективная ) работа для чего-то, что можно так же легко решить с помощью хорошо известного формата обмена / хранения, такого как JSON.

Я собирался поставить « можно, но … », однако я просто скажу еще раз: « Ты не можешь » 2

2 Не делайте

Конечно, вы можете делать это с помощью регулярных выражений.

 preg_match_all( '/([^\s]+)\s*{((?:[^{}]*|(?R))*)}/', $yourStuff, $matches, PREG_SET_ORDER ); 

Это дает мне следующие в матчах:

 [1]=> string(5) "alpha" [2]=> string(46) " beta { charlie; } delta; " 

а также

 [1]=> string(7) "foxtrot" [2]=> string(22) " golf; hotel; " 

Немного сломал.

 ([^\s]+) # non-whitespace (block name) \s* # whitespace (between name and block) { # literal brace ( # begin capture (?: # don't create another capture set [^{}]* # everything not a brace |(?R) # OR recurse )* # none or more times ) # end capture } # literal brace 

Только для вашей информации это отлично работает на n-глубоких уровнях фигурных скобок.

Я думаю, что вы можете получить что-то, используя preg_split , сопоставив [a-zA-Z0-9][:blank]+{ и } . Вы сможете построить свой массив, пройдя результат. Используйте рекурсивную функцию, которая идет глубже, когда вы сопоставляете открывающий тег, а верхний – с закрывающим тегом.

В противном случае самым чистым решением будет реализация грамматики ANTLR !