Как исправить регулярное выражение BBcode - PullRequest
3 голосов
/ 11 августа 2011

У меня есть регулярное выражение, которое захватывает теги BBcode. Отлично работает, за исключением незначительного сбоя.

Вот текущее выражение:

\[([^=\[\]]+)[=\x22']*([^ \[\]]*)['\x22]*\](.+)\[/\1\]

Вот текст, с которым он успешно сопоставляет, и группы, которые он строит:

[url = http://www.google.com]Go в Google! [/ Url]
1: URL
2: http://www.google.com
3: Перейти в Google!

[IMG] http://www.somesite.com/someimage.jpg[/img]
1: img
2: NULL
3: http://www.somesite.com/someimage.jpg

[цитата] [цитата] первая вложенная цитата [/ цитата] [цитата] вторая вложенная цитата [/ цитата] [/ цитата]
1: цитата
2: NULL
3: [цитата] первая вложенная цитата [/ цитата] [цитата] вторая вложенная цитата [/ цитата]

Все это замечательно. Я могу обработать вложенные теги, запустив 3-ю группу совпадений с одним и тем же регулярным выражением, и рекурсивно обработать все вложенные теги. Проблема в примере с использованием тегов [quote]. Обратите внимание, что третья группа совпадений представляет собой набор из двух тегов кавычек, поэтому мы ожидаем двух совпадений. Тем не менее, мы получаем один матч, например:

[цитата] первая вложенная цитата [/ цитата] [цитата] вторая вложенная цитата [/ цитата]
1: цитата
2: NULL
3: первая вложенная цитата [/ quote] [quote] вторая вложенная цитата

Ааааа! Это совсем не то, что мы хотели. Существует довольно простой способ исправить это, я изменяю регулярное выражение из этого:

\[([^=\[\]]+)[=\x22']*([^ \[\]]*)['\x22]*\](.+)\[/\1\]

На это:

\[([^=\[\]]+)[=\x22']*([^ \[\]]*)['\x22]*\](((?!\[/\1\]).)+)\[/\1\]

Добавляя ((?!\[/\1\]).), мы делаем недействительным все совпадение, если третья группа совпадений содержит закрывающий тег BBcode. Так что теперь это работает, мы получаем два матча:

[цитата] первая вложенная цитата [/ цитата] [цитата] вторая вложенная цитата [/ цитата]

[цитата] первая вложенная цитата [/ цитата]
1: цитата
2: NULL
3: первая вложенная цитата

[цитата] вторая вложенная цитата [/ цитата]
1: цитата
2: NULL 3: вторая вложенная цитата

Я был счастлив, что исправил это, но теперь у нас есть другая проблема. Это новое регулярное выражение не выполняется в первом, где мы вкладываем два тега цитаты в один больший тег цитаты. Мы получаем два матча вместо одного:

[цитата] [цитата] первая вложенная цитата [/ цитата] [цитата] вторая вложенная цитата [/ цитата] [/ цитата]

[цитата] [цитата] первая вложенная цитата [/ цитата]
1: цитата
2: NULL
3: [цитата] первая вложенная цитата

[quote] вторая вложенная цитата [/ quote]
1: цитата
2: NULL
3: вторая вложенная цитата

Первое совпадение неверно, а второе совпадение, хотя оно и правильно сформировано, не является желаемым совпадением. Мы хотели получить одно большое совпадение, а третья группа совпадений - это два вложенных тега кавычек, как, например, когда мы использовали первое выражение.

Есть предложения? Если я смогу преодолеть этот пробел, у меня должно быть довольно мощное выражение BBcode.

1 Ответ

4 голосов
/ 11 августа 2011

Используя балансирующие группы , вы можете построить регулярное выражение следующим образом:

(?>
  \[ (?<tag>[^][/=\s]+) \s*
  (?: = \s* (?<val>[^][]*) \s*)?
  ]
)

(?<content>
  (?>
    \[(?<innertag>[^][/=\s]+)[^][]*]
    |
    \[/(?<-innertag>\k<innertag>)]
    |
    [^][]+
  )*
  (?(innertag)(?!))
)

\[/\k<tag>]

Упрощено по примеру Коби.


В следующем:

[foo=bar]baz[/foo]
[b]foo[/b]
[i][i][foo=bar]baz[/foo]foo[/i][/i]
[i][i][i][i]foo[/i][/i][/i][i][i]foo[/i][/i][/i]
[quote][quote][b][img]foo[/img][b]bold[/b][b][b]deep[/b][/b][/b][/quote]bar[quote]baz[/quote][/quote]

Находит эти совпадения:

  • [foo=bar]baz[/foo]
  • [b]foo[/b]
  • [i][i][foo=bar]baz[/foo]foo[/i][/i]
  • [i][i][i][i]foo[/i][/i][/i][i][i]foo[/i][/i][/i]
  • [quote][quote][b][img]foo[/img][b]bold[/b][b][b]deep[/b][/b][/b][/quote]bar[quote]baz[/quote][/quote]

Полный пример на http://ideone.com/uULOs

(Старая версия http://ideone.com/AXzxW)

...