Удалить вложенные кавычки

У меня есть этот текст, и я пытаюсь удалить все внутренние кавычки, просто сохраняя один уровень цитирования. Текст внутри кавычек содержит любые символы, даже перевод строки и т. д. Возможно ли это с помощью регулярного выражения или мне нужно написать небольшой парсер?

[quote=foo]I really like the movie. [quote=bar]World 

War Z[/quote] It's amazing![/quote]
This is my comment.
[quote]Hello, World[/quote]
This is another comment.
[quote]Bye Bye Baby[/quote]

Вот текст, который я хочу:

[quote=foo]I really like the movie.  It's amazing![/quote]
This is my comment.
[quote]Hello, World[/quote]
This is another comment.
[quote]Bye Bye Baby[/quote]

Это регулярное выражение, которое я использую в PHP:

%\[quote\s*(=[a-zA-Z0-9\-_]*)?\](.*)\[/quote\]%si

Я пробовал и этот вариант, но он не соответствует . или ,, и я не могу понять, что еще я могу найти внутри цитаты:

%\[quote\s*(=[a-zA-Z0-9\-_]*)?\]([\w\s]+)\[/quote\]%i

Проблема находится здесь:

(.*)

person noun    schedule 12.09.2013    source источник
comment
В чем проблема с (.*)? не совпадают новые строки?   -  person justhalf    schedule 12.09.2013
comment
Знаете ли вы о php.net/manual/en/book.bbcode.php ?   -  person Andy Lester    schedule 12.09.2013
comment
@justhalf Проблема в том, что он не останавливается на правильной цитате, а идет на последней.   -  person noun    schedule 12.09.2013
comment
@andy Я проверю это, кажется жизнеспособным решением, мне нужно написать код, чтобы проверить его.   -  person noun    schedule 12.09.2013


Ответы (3)


Вы можете использовать это:

$result = preg_replace('~\G(?!\A)(?>(\[quote\b[^]]*](?>[^[]+|\[(?!/?quote)|(?1))*\[/quote])|(?<!\[)(?>[^[]+|\[(?!/?quote))+\K)|\[quote\b[^]]*]\K~', '', $text);

Детали:

\G(?!\A)              # contiguous to a precedent match
(?>                   ## content inside "quote" tags at level 0
  (                    ## nested "quote" tags (group 1)
    \[quote\b[^]]*]
    (?>                ## content inside "quote" tags at any level
      [^[]+
     |                  # OR
      \[(?!/?quote)
     |                  # OR
      (?1)              # repeat the capture group 1 (recursive)
    )*
    \[/quote]
  )
 |
  (?<!\[)           # not preceded by an opening square bracket
  (?>              ## content that is not a quote tag
    [^[]+           # all that is not a [
   |                # OR
    \[(?!/?quote)   # a [ not followed by "quote" or "/quote"
  )+\K              # repeat 1 or more and reset the match
)
|                   # OR
\[quote\b[^]]*]\K   # "quote" tag at level 0 
person Casimir et Hippolyte    schedule 12.09.2013
comment
Я пробовал это, но это не работает, потому что он удаляет две последние кавычки, а не только внутреннюю. - person noun; 12.09.2013
comment
Что я могу сказать? Ты жжешь! Спасибо, это работает в любых условиях. :-) - person noun; 12.09.2013

использовать этот шаблон

\[quote=?[^\]]*\][^\[]*\[/quote\](?=((.(?!\[q))*)\[/)

и заменить ничего подобного в этом примере

person alpha bravo    schedule 12.09.2013
comment
Все еще проблема здесь, но хорошее начало. Это не работает, если у меня есть другие теги внутри кавычек. Например, если у меня есть [b]foo[/b] внутри кавычек. Извините, я не упомянул, что внутри цитаты у меня могут быть любые теги форматирования. - person noun; 12.09.2013

Я думаю, что было бы проще написать парсер.

Используйте регулярное выражение, чтобы найти [quote] и [\quote], а затем проанализируйте результат.

preg_match_all('#(\[quote[^]]*\]|\[\/quote\])#', $bbcode, $matches, PREG_OFFSET_CAPTURE);
$nestlevel = 0;
$cutfrom = 0;
$cut = false;
$removed = 0
foreach($matches(0) as $quote){
    if (substr($quote[0], 0, 1) == '[') $nestlevel++; else $nestlevel--;
    if (!$cut && $nestlevel == 2){ // we reached the first nested quote, start remove here
        $cut = true;
        $cutfrom = $quote[1];
    }
    if ($cut && $nestlevel == 1){ // we closed the nested quote, stop remove here
        $cut = false;
        $bbcode = substr_replace($bbcode, '', $cutfrom - $removed, $quote[1] + 8 - $removed); // strlen('[\quote]') = 8
        $removed += $quote[1] + 8 - $cutfrom;
    }
);
person Lorenz Meyer    schedule 12.09.2013
comment
У меня есть идея, но приведенное выше регулярное выражение ничего не соответствует. - person noun; 12.09.2013
comment
Извините, не хватало скобки. Я исправил. Я также не тестировал код PHP, надеюсь, внутри нет другой ошибки. - person Lorenz Meyer; 12.09.2013
comment
да. Регулярные выражения лучше всего подходят для соответствия грамматике обычного языка, отсюда и название. Вложенный тег не соответствует этому критерию (по той же причине вы не можете анализировать html с помощью регулярного выражения). - person troelskn; 12.09.2013