Обработка Javascript RegEx submatches - PullRequest
       12

Обработка Javascript RegEx submatches

1 голос
/ 17 сентября 2008

Я пытаюсь написать JavaScript RegEx для замены пользовательских тегов реальными HTML-тегами, поэтому [b] станет <b> и так далее. RegEx, который я использую, выглядит так

var exptags = /\[(b|u|i|s|center|code){1}]((.){1,}?)\[\/(\1){1}]/ig;

со следующим JavaScript

s.replace(exptags,"<$1>$2</$1>");

это прекрасно работает для одиночных вложенных тегов, например:

[b]hello[/b] [u]world[/u]

но если теги вложены друг в друга, они будут соответствовать только внешним тегам, например

[b]foo [u]to the[/u] bar[/b]

это будет соответствовать только тегам b. Как я могу это исправить? Должен ли я просто зациклить, пока начальная строка не совпадает с результатом? У меня такое чувство, что Паттен ((.){1,}?) тоже не так?

Спасибо

Ответы [ 8 ]

3 голосов
/ 17 сентября 2008

Самым простым решением было бы заменить все теги, независимо от того, закрыты они или нет, и позволить .innerHTML сработать, если они совпадают или нет, так будет намного более устойчиво.

var tagreg = /\[(\/?)(b|u|i|s|center|code)]/ig
div.innerHTML="[b][i]helloworld[/b]".replace(tagreg, "<$1$2>") //no closing i
//div.inerHTML=="<b><i>helloworld</i></b>"
1 голос
/ 17 сентября 2008

AFAIK Вы не можете выразить рекурсию с помощью регулярных выражений.

Однако вы можете сделать это с помощью .NET System.Text.RegularExpressions, используя сбалансированное сопоставление. Подробнее здесь: http://blogs.msdn.com/bclteam/archive/2005/03/15/396452.aspx

Если вы используете .NET, вы можете реализовать то, что вам нужно, с помощью обратного вызова. Если нет, вам, возможно, придется развернуть свой собственный маленький анализатор JavaScript.

Опять же, если вы можете позволить себе попасть на сервер, вы можете использовать полный анализатор. :)

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

0 голосов
/ 17 сентября 2008

Как насчет:

tagreg=/\[(.?)?(b|u|i|s|center|code)\]/gi;
"[b][i]helloworld[/i][/b]".replace(tagreg, "<$1$2>");
"[b]helloworld[/b]".replace(tagreg, "<$1$2>");

Для меня вышеперечисленное производит:

<b><i>helloworld</i></b>
<b>helloworld</b>

Это, кажется, делает то, что вы хотите, и имеет преимущество в том, что требуется только один проход.

Отказ от ответственности: я не часто пишу в JS, поэтому, если я допустил какие-либо ошибки, пожалуйста, не стесняйтесь указывать на них: -)

0 голосов
/ 17 сентября 2008

Согласен с Ричардом Сзалайем, но его регулярное выражение не цитируется правильно:

var exptags = /\[(b|u|i|s|center|code)](.*)\[\/\1]/ig;

чище. Обратите внимание, что я также изменяю .+? на .*. Есть две проблемы с .+?:

  1. вы не будете совпадать с [u] [/ u], поскольку между ними нет хотя бы одного символа (+)
  2. не жадное совпадение не будет так хорошо работать с тем же тегом, вложенным в себя (?)
0 голосов
/ 17 сентября 2008

Причина, по которой вложенный блок не заменяется, состоит в том, что совпадение для [b] ставит позицию после [/ b]. Таким образом, все, что соответствует ((.) {1,}?), Игнорируется.

Можно написать рекурсивный парсер на стороне сервера - Perl использует qr // , а в Ruby, вероятно, есть что-то похожее.

Хотя вам не обязательно нужен настоящий рекурсив. Вы можете использовать относительно простой цикл для эквивалентной обработки строки:

var s = '[b]hello[/b] [u]world[/u] [b]foo [u]to the[/u] bar[/b]';
var exptags = /\[(b|u|i|s|center|code){1}]((.){1,}?)\[\/(\1){1}]/ig;

while (s.match(exptags)) {
   s = s.replace(exptags, "<$1>$2</$1>");
}

document.writeln('<div>' + s + '</div>'); // after

В этом случае он сделает 2 прохода:

0: [b]hello[/b] [u]world[/u] [b]foo [u]to the[/u] bar[/b]
1: <b>hello</b> <u>world</u> <b>foo [u]to the[/u] bar</b>
2: <b>hello</b> <u>world</u> <b>foo <u>to the</u> bar</b>

Также, несколько советов по очистке RegEx:

var exptags = /\[(b|u|i|s|center|code)\](.+?)\[\/(\1)\]/ig;
  • {1} предполагается, когда не существует других спецификаторов подсчета
  • {1,} можно сократить до +
0 голосов
/ 17 сентября 2008

Вы можете просто несколько раз применить регулярное выражение, пока оно не перестанет совпадать. Это будет делать странные вещи, такие как "[b] [b] foo [/ b] [/ b]" => " [b] foo [/ b]" => " foo ", но, насколько я вижу, конечный результат все равно будет разумной строкой с соответствующими (хотя и не обязательно правильно вложенными) тегами.

Или, если вы хотите сделать это «правильно», просто напишите простой парсер рекурсивного спуска. Хотя люди могут ожидать, что «[b] foo [u] bar [/ b] baz [/ u]» сработает, что сложно определить с помощью парсера.

0 голосов
/ 17 сентября 2008

Вы правы в том, что внутренний паттерн неприятен.

((.){1,}?)

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

Вы также захватываете имя вашего закрывающего элемента, когда оно вам не нужно, и используете {1}, когда это подразумевается. Ниже приведена версия для очистки:

/\[(b|u|i|s|center|code)](.+?)\[\/\1]/ig

Не уверен насчет другой проблемы.

0 голосов
/ 17 сентября 2008

Да, вам придется зацикливаться. В качестве альтернативы, поскольку ваши теги очень похожи на HTML, вы можете заменить [b] для <b> и [/b] для </b> по отдельности. (.) {1,}? такой же, как (. *?) - то есть любые символы, наименьшая возможная длина последовательности.

Обновлено: спасибо MrP, (.) {1,}? это (.) + ?, мой плохой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...