Я пытаюсь заставить работать PHP регулярное выражение, которое анализирует строку для текста в скобках, игнорируя при этом возможные вложенные скобки:
Допустим, я хочу
Lorem ipsum [1. dolor sit amet, [consectetuer adipiscing] elit.]. Aenean commodo ligula eget dolor.[2. Dolor, [consectetuer adipiscing] elit.] Aenean massa[3. Lorem ipsum] dolor.
возвращать
[1] => "dolor sit amet, [consectetuer adipiscing] elit."[2] => "Dolor, [consectetuer adipiscing] elit."[3] => "Lorem ipsum"
Пока я получил
'/\[([0-9]+)\.\s([^\]]+)\]/gi'
но он ломается, когда появляются вложенные скобки. Посмотреть демо
Как я могу игнорировать внутренние скобки от обнаружения?
Спасибо заранее!
Вы можете использовать этот шаблон, который фиксирует номер элемента и следующий текст в двух разных группах. Если вы уверены, что все номера позиций уникальны, вы можете построить ассоциативный массив, описанный в вашем вопросе, с помощью простого array_combine
:
$pattern = '~\[ (?:(\d+)\.\s)? ( [^][]*+ (?:(?R) [^][]*)*+ ) ]~x';
if (preg_match_all($pattern, $text, $matches))
$result = array_combine($matches[1], $matches[2]);
Детали шаблона:
~ # pattern delimiter
\[ # literal opening square bracket
(?:(\d+)\.\s)? # optional item number (*)
( # capture group 2
[^][]*+ # all that is not a square bracket (possessive quantifier)
(?: #
(?R) # recursion: (?R) is an alias for the whole pattern
[^][]* # all that is not a square bracket
)*+ # repeat zero or more times (possessive quantifier)
)
] # literal closing square bracket
~x # free spacing mode
(*) обратите внимание, что часть номера элемента должна быть необязательной, если вы хотите использовать рекурсию с (?R)
(например [consectetuer adipiscing]
не имеет номер элемента.). Это может быть проблематично, если вы хотите избежать квадратных скобок без номера позиции. В этом случае вы можете построить более надежный шаблон, если вы измените необязательную группу (?:(\d+)\.\s)?
условному заявлению: (?(R)|(\d+)\.\s)
Условный оператор:
(?(R) # IF you are in a recursion
# THEN match this (nothing in our case)
| # ELSE
(\d+)\.\s #
)
Таким образом, номер позиции становится обязательным.
Вы можете использовать рекурсивные ссылки на предыдущие группы:
(?<no_brackets>[^\[\]]*){0}(?<balanced_brackets>\[\g<no_brackets>\]|\[(?:\g<no_brackets>\g<balanced_brackets>\g<no_brackets>)*\])
Идея состоит в том, чтобы определить желаемые совпадения как нечто без скобок, окруженное []
или что-то, что содержит последовательность без скобок или сбалансированных скобок с первым правилом.
Вы можете использовать рекурсивное регулярное выражение для получения всех подстрок, заключенных в квадратные скобки, а затем использовать preg_replace
внутри array_map
снять кронштейны и закрывающие кронштейны:
$str = "Lorem ipsum [1. dolor sit amet, [consectetuer adipiscing] elit.]. Aenean commodo ligula eget dolor.[2. Dolor, [consectetuer adipiscing] elit.] Aenean massa[3. Lorem ipsum] dolor.";
preg_match_all('/\[(?>[^\[\]]|(?R))*]/', $str, $matches);
$res = array_map(function($el) {
return preg_replace('/^\[\d+\.(.*?)\s*\]$/s', '$1', $el);
},
$matches[0]);
print_r($res);
Увидеть IDEONE демо
\[(?>[^\[\]]|(?R))*]
регулярные выражения [
тогда ничего кроме [
а также ]
или вложенный [...]
строит. Узнайте больше о рекурсии с регулярным выражением в regular-expressions.info. Здесь regex demo.
Регулярное выражение внутри preg_repace
— ^\[\d+\.(.*?)\s*\]$
— будет соответствовать начальному [
с 1 или более цифрами и точкой после, а также сопоставить и захватить остальные до последнего необязательного пробела (\s*
) и закрытие ]
( $
убедитесь, что скобка совпадает в конце строки). С $1
мы можем восстановить оставшуюся часть строки и использовать ее для заполнения нового массива. Увидеть 2-е демо здесь.