PHP разбить строку по символу с исключением
Есть к примеру такая строка 123|@rand(тест {@rand(номер 1234|номер 4321)}|строка||. Это строка параметров функции в тексте. (функция выглядит, как {@funcname(param1|param2)}). Каждый параметр отделяется знаком |
И мне нужно получить из этой строки массив с параметрами. Т.е. разбить строку по символу |.
Главная проблема в том, что в строке может содержаться вызов функции с такой же строкой параметров, в которой тоже содержится знак |. Отдельный вызов функции НЕЛЬЗЯ ДЕЛИТЬ!
Итого нужно разбить строку выше на такой массив
array(
123,
'@rand(тест {@rand(номер 1234|номер 4321)}',
строка,
'', // || считать, как пустая строка
''
);
Помогите составить регулярное выражение!
Я накидал что-то такое, но это работает неправильно - "/[^({@\w+\()]\|[^(\)})]/ui
Ответы (2 шт):
В свете более подробного разъяснения автором задачи, я бы предложил новое решение.
//Во-первых в приведенном примере автор немного ошибся и забыл у первого `@rand` в конце закрывающую скобку я ее добавил, если это не так - поправьте строку ну и регулярку
$text = "123|@rand(тест {@rand(номер 1234|номер 4321)})|строка||";
//Разбитие по `|` приведет к тому, что Вы в выходном массиве получите еще и разбитые параметры функции `@rand`. Что как Вы сами пишите и является проблемой которую Вам в том числе нужно решить
//Поэтому я бы сделал так. В строке которая содержит параметры `@rand(тест {@rand(номер 1234|номер 4321)})` вначале заменил `|` на какой-то другой символ.
if ( preg_match_all("/(@(\S+)\((.*)\))/", $text, $matches) && is_array( $matches ) && count($matches) > 0 && is_array($matches[0]) && count($matches[0]) > 0) {
//Меняем `|` на `&`
$rtext = preg_replace("/\|/", "&", $matches[0][0]);
//preg_quotes экранирует спец.символы использующиеся в регулярных выражениях - а их у нас в полученной строке полно
$text = preg_replace("/".preg_quote($matches[0][0])."/", $rtext, $text);
//После чего спокойно разбил всю исходную строку `$text`, по символу `|`
$params = explode("|", $text);
if ( is_array( $params ) && count( $params ) > 0 ) {
//Далее идем по массиву и ищем элемент-строку начинающийся на '@'
foreach( $params as $p ) {
$p = trim( $p );
if ( mb_substr( $p, 0, 1) == '@' ) {
//@rand(тест {@rand(номер 1234&номер 4321)})
//А теперь парсим эту строку на содержание параметров
//Если кол-во параметров другое или формат строки отличается - подправьте регулярку
if ( preg_match_all("/^\@\S+\(\W+\{\@\S+\((.+?)\&(.+?)\)\}\)/", $p, $matches) && is_array( $matches ) && count($matches) > 0 && is_array($matches[0]) && count($matches[0]) > 0) {
$param1 = $matches[1][0]."\n"; //номер 1234
$param2 = $matches[2][0]."\n"; //номер 4321
}
}
}
}
}
Используйте
preg_split('~{@\w+\(.*?\)}(?![^|])(*SKIP)(*F)|\|~u', $text)
{@- текст{@\w+- одна и более букв, цифр или знак_\(- символ(.*?- ноль или более символов, отличных от символа перевода строки, как можно меньше\)}- текст)}(?![^|])- сразу после текущей позиции должен быть символ|или конец строки(*SKIP)(*F)- в текущей позиции сигнализирует конец совпадения, которое отменяется, а поиск нового совпадения начинается именно с текущей позиции|- или\|- символ|.
Пример кода на PHP:
$text = "123|@rand(тест {@rand(номер 1234|номер 4321)}|строка||";
print_r(preg_split('~{@\w+\(.*?\)}(?![^|])(*SKIP)(*F)|\|~u', $text));
Результат:
Array
(
[0] => 123
[1] => @rand(тест {@rand(номер 1234|номер 4321)}
[2] => строка
[3] =>
[4] =>
)