У меня есть файл, который структурирован в большой многомерной структуре, похожей на 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 !