Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Парсинг BBcode (https://javascript.ru/forum/misc/24939-parsing-bbcode.html)

trikadin 22.01.2012 19:31

Цитата:

Сообщение от Gozar
на скорости правда это не отразилось, может мало циклов,

Да и не должно было - тут же происходит каждый раз два переприсвоения в цикле) А в твоём варианте - только одно)

devote 23.01.2012 13:34

да, что-то более лучшего ничего не выходит, только так:

var text = 'test [b]blah [i]tata[/i] blah[/b] param [quote="test"]lal[alala[/quote] qweqweqwe [u]ter[u]tet[/u]er[/u] hjf [u]sdhg[/u] end';

var re = /(\[\s*([a-z]+)\s*(?:=?\s*([^\]]+))?\])([^\[]+)(\[\/\2\])/i,
    next = true, step = 1;

while( next ) {
    next = false;
    text = text.replace( re, function( all, tagattr, tag, attr, content, close ) {
        next = true;

        alert( [tagattr, tag, attr, content, close] );

        return '<' + tag + '>' + content + '</' + tag + '>';
    });
    if ( !next && step++ == 1 ) {
        next = true;
        re = /(\[\s*([a-z]+)\s*(?:=?\s*([^\]]+))?\])(.*?)(\[\/\2\])/i;
    }
}

alert( text );

Deff 05.01.2016 01:37

var text = 'test [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda[/quote] qweqweqwe [u]ter[u]t[/quote]et[/u]er[/u] hjf [u]sdhg[/u] end';

var chang = '¬'; //Строка замены для "["
var re = /(\[([a-z]+)(?:="?([^\]]+?))?"?\])([^\[]*)(\[\/\2\])/gi,
    next = true, step = 1;

while( next ) {
    next = false;
    if(step==1)text = text.replace( re, function( all, tagattr, tag, attr, content, close ) {
        next = true;

        //alert( [step,tagattr, tag, attr, content, close] );

        return '<' + tag + '>' + content + '</' + tag + '>';
    });
    if ( !next) {step = 2;
        text = text.replace(/^([\s\S]*?)(\[\/([a-z]+)\])([\s\S]*)$/i, function(str,strST,tagEnd, tag, strEnd) {
           next = true;
           var end = strST.length,
           st = strST.lastIndexOf('['+tag);
           if(st<0) return strST + chang+'/'+ tag + ']' + strEnd;

           var find = text.substring(st,end),
           rega = '^\\['+tag+'(?:="?([^\\[\\]]+?))?"?\\]([\\s\\S]*)$';

           var found = find.replace(RegExp(rega,'i'), function(str,attr,content) {return '<'+tag+'>'+ content.replace(/\[/g, chang) + '</'+tag+'>'});

           return text.substr(0,st) + (found!=find ? found : chang + find.slice(1)+ tagEnd) + strEnd;

      })
   }
}

text = text.replace(RegExp(chang,'g'),'['); //возвращаем скобку;
alert( text );


Чуть ускорил, сделав первый проход глобальным поиском (/gi),
На Втором проверка неполных вложенных тегов (Исключаем ситуации типа [div][li][/div][/li]; подменяем у них символ \[)
===================
В принципе вторая часть работает и самостоятельно, но совместно с первой, получаем приличное ускорение на тегах без квадратных скобок внутри

trikadin 05.01.2016 21:11

Deff, ты его четыре года писал? :D

Яростный Меч 05.01.2016 21:29

Понимаю что тема старая :)
но раз уж всплыла, то вот мой вариант. Здесь в упрощенном виде, только самые простые теги, чтобы нагляднее была идея. А идея вот в чем: когда натыкаемся на открывающий тег, кладем его в стек. Если встретили закрывающий, то проверяем его соответствие верхушке стека, если не соответствует, оставляем бб-код, иначе ставим закрывающий и убираем со стека. В конце, если что-то осталось на стеке, добиваем "закрывашками".

var parseBB = function(str) {
    if (!str) { return str; }
    var rx = /\[(\/?)(b|i|u|s)\s*\]/gi;
    var stack = [];
    str = str.replace(rx, function(m, close, tag) {
        tag = tag.toLowerCase();
        if (close) {
            if (!stack.length || (stack[stack.length - 1] !== tag)) {
                return m;
            }
            stack.pop();
        } else {
            stack.push(tag);
        }
        return "<" + close + tag + ">";
    });
    if (stack.length) {
        str = str + stack.reverse().map(function(tag) { 
            return "</" + tag + ">";
        }).join("");
    }
    return str;
};

Deff 07.01.2016 01:43

trikadin,
Нет, у мну 4 дня назад встала тема быстрого предпросмотра сообщений в топике(точнее мгновенного, по мере ввода), поискал в инете, наткнулся на эту тему, остальные Варианты были хуже

Deff 07.01.2016 01:46

Цитата:

Сообщение от Яростный Меч
иначе ставим закрывающий и убираем со стека. В конце, если что-то осталось на стеке, добиваем "закрывашками".

У мну лучше, 1. Решена проблема пересекающихся(или неполных) тегов
2. Задача по топику была делать не рекурсией(кушает много памяти при длинных строках с много тегами), а простым повтором через while
3. Нун вначале искать первый тег закрытия от начала строки и двигаться вверх до его "открывашки", только тогда не будет пересекающихся тегов(или конфликтов), если вдобавок на всём пути, пройденного до найденной "открывашки", убиваем(подменяем, с возвратом в конце распарса) все открывающиеся скобки "[" , ибо внутри не может быть валидных вложенных ВВ-тегов по определению.
Если "открывашка" не найдена, убиваем открывающуюся скобку "[" у найденного первоначального тега-"закрывашки" .
Крутим цикл пока не будут найдены все('ближайшие от начала') "закрывашки" ...

Deff 07.01.2016 03:01

Яростный Меч,
Тест: (тег [quote="test2"] - валидный - пропущен, по идее валидный и первый, но тут можно оспорить, ты сделал валидными теги [u]
var parseBB = function(str) {
    if (!str) { return str; }
    var rx = /\[(\/?)(b|i|u|s)\s*\]/gi;
    var stack = [];
    str = str.replace(rx, function(m, close, tag) {
        tag = tag.toLowerCase();
        if (close) {
            if (!stack.length || (stack[stack.length - 1] !== tag)) {
                return m;
            }
            stack.pop();
        } else {
            stack.push(tag);
        }
        return "<" + close + tag + ">";
    });
    if (stack.length) {
        str = str + stack.reverse().map(function(tag) { 
            return "</" + tag + ">";
        }).join("");
    }
    return str;
};
var text = 'test [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda[/quote] qweqweqwe [u]ter[u]t[/quote]et[/u]er[/u] hjf [u]sdhg[/u] end';
alert(parseBB(text));
//Убрал атрибуты, сменил quote на s //Да, тут терпимо, но: у [/s] - концовка осталась (ошибки в закрывашках критичны - цитата - тег <div> убила бы страницу)
var text2 = 'test [b]blah\n\ [i]tata[/i] blah[/b] param [s]d[s]lalsd[li]fnsdf[alalsdfsda[/s] qweqweqwe [u]ter[u]t[/s]et[/u]er[/u] hjf [u]sdhg[/u] end';
alert(parseBB(text2))

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

Яростный Меч 07.01.2016 03:23

Цитата:

Сообщение от Deff (Сообщение 402843)
1. Решена проблема пересекающихся(или неполных) тегов

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

Цитата:

Сообщение от Deff (Сообщение 402843)
2. Задача по топику была делать не рекурсией(кушает много памяти при длинных строках с много тегами), а простым повтором через while

В моём случае всё делается за один replace. Т.е. по затратам памяти имеем только исходную строку и результат.


----
На форумах бывает тег оформления исходного кода, в котором теги [b] и т.п. вообще не заменяются. Твой способ сумеет такое поддержать? Мой - запросто, если при нахождении такого тега включать специальный режим "незамены".

Deff 07.01.2016 03:41

Яростный Меч,
Ну идея была такая: вкладывать в тег перепарсенной "открывашки" атрибут data-исходник в котором прописывать исходный код полного bb-teга в еncode, ибо парсятся все (а у меня есть и таблицы) если какой то тег в табле отсутствует, то нун возвращать теги в BB-код, что при JQ делается парой строк
На самом деле будут возвращаться все(до распарса в HTML), не прописанные в объекте перепарса (это на этапе самого распарса, не стал усложнять код незначимыми подробностями) Ибо у меня много кодов трансформируемых в несколько тегов и вставкой атрибутов
Вот эта функция будет браться из объекта с описанными функциями распарса по тегам
var found = find.replace(RegExp(rega,'i'), function(str,attr,content) {return '<'+tag+'>'+ content.replace(/\[/g, chang) + '</'+tag+'>'});


Часовой пояс GMT +3, время: 00:50.