PHP regex для обнаружения текста внутри скобок, игнорируя вложенные скобки

Я пытаюсь сделать PHP regex, который анализирует строку для текста в скобках, игнорируя возможные вложенные скобки:

допустим, я хочу!--4-->

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'

но он ломается, когда возникают вложенные скобки. посмотреть демо

как я могу игнорировать внутренние скобки от обнаружения? Заранее спасибо!

3 ответов


вы можете использовать этот шаблон, который фиксирует номер лота и следующий текст в двух разных группах. Если вы уверены, что все номера элементов уникальны, вы можете построить ассоциативный массив, описанный в вашем вопросе, с помощью простого 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', '', $el); 
    },
    $matches[0]);
print_r($res);

посмотреть IDEONE demo

на \[(?>[^\[\]]|(?R))*] regex матчи [, тогда что угодно, кроме [ и ] или вложенный элемент [...] конструктов. См. больше о рекурсии с regex at regular-expressions.info. Вот это регулярное выражение демо.

регулярное выражение внутри preg_repace - ^\[\d+\.(.*?)\s*\]$ - будет соответствовать инициалу [ С 1 или более цифр и период после, и матч и захватить остальные до окончательного необязательного пробела (\s*) и закрывающим тегом ] (the $ убедитесь, что скобка соответствует в конце строки). С мы можем восстановить остальную часть строки и использовать ее для заполнения нового массива. Вижу 2-я демонстрация regex здесь.