Цитата:
|
да, что-то более лучшего ничего не выходит, только так:
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 ); |
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]; подменяем у них символ \[) =================== В принципе вторая часть работает и самостоятельно, но совместно с первой, получаем приличное ускорение на тегах без квадратных скобок внутри |
Deff, ты его четыре года писал? :D
|
Понимаю что тема старая :)
но раз уж всплыла, то вот мой вариант. Здесь в упрощенном виде, только самые простые теги, чтобы нагляднее была идея. А идея вот в чем: когда натыкаемся на открывающий тег, кладем его в стек. Если встретили закрывающий, то проверяем его соответствие верхушке стека, если не соответствует, оставляем бб-код, иначе ставим закрывающий и убираем со стека. В конце, если что-то осталось на стеке, добиваем "закрывашками". 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; }; |
trikadin,
Нет, у мну 4 дня назад встала тема быстрого предпросмотра сообщений в топике(точнее мгновенного, по мере ввода), поискал в инете, наткнулся на эту тему, остальные Варианты были хуже |
Цитата:
2. Задача по топику была делать не рекурсией(кушает много памяти при длинных строках с много тегами), а простым повтором через while 3. Нун вначале искать первый тег закрытия от начала строки и двигаться вверх до его "открывашки", только тогда не будет пересекающихся тегов(или конфликтов), если вдобавок на всём пути, пройденного до найденной "открывашки", убиваем(подменяем, с возвратом в конце распарса) все открывающиеся скобки "[" , ибо внутри не может быть валидных вложенных ВВ-тегов по определению. Если "открывашка" не найдена, убиваем открывающуюся скобку "[" у найденного первоначального тега-"закрывашки" . Крутим цикл пока не будут найдены все('ближайшие от начала') "закрывашки" ... |
Яростный Меч,
Тест: (тег [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)) Вдобавок у меня, при парсе на лету, когда юзер не закончил строку, желателен максимальный распарс тегов //Тут ксать у мну и таблицы еще есть, пока не додумал как парсить исключения-ошибки отсутствия некоторых тегов) |
Цитата:
Цитата:
---- На форумах бывает тег оформления исходного кода, в котором теги [b] и т.п. вообще не заменяются. Твой способ сумеет такое поддержать? Мой - запросто, если при нахождении такого тега включать специальный режим "незамены". |
Яростный Меч,
Ну идея была такая: вкладывать в тег перепарсенной "открывашки" атрибут 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+'>'}); |
Цитата:
|
laimas,
Однотеговых BB-кодов мало <br> <img> [you] (часто есть - имя юзера) Они парсятся первыми, замечание справедливое, но я описал, что выходная функция HTML тегов должна быть в итоге привязана к объекту выходных функций по тегам, ибо часто теги преобразуются в многотеговые конструкции HTML Опять же и атрибуты меняют итоговый код HTML |
Цитата:
|
laimas,
Вообще тема интересная, рыл тут четыре дня, вот лучший пост был этот от devote (Были пару интересных импортных постов, но там код для анализа ошибок на страницу Думаю в принципе, задача поиска без ошибок без движения челноком по строке (ищем закрывашку и идём от нёё вверх к открывашке) решается весьма трудоёмко. Пока реальных кодов с учётом всех ошибок мизер.. |
Да, были темы на форуме по использованию принципа стека, правда не в контексте ВВ, но суть та же.
Цитата:
Если подумать, то думаю можно использовать стек и как источник "места" ошибок, делать откат и удалять некорректное. |
laimas,
Ну в итоге, совместно с куском от dеvote мой скрипт парсит быстее - проверил на мегабайте BB-кодов (Правдо мало вставлял лишних квадратных скобок, спецом ошибок в BB-кодах 20% (если интересно завтра выложу тест |
Выкладывайте, правда ВВ код можно обработать и на сервере, и для этого есть готовое на Cи. Я не сторонник взваливать все на клиента. )
|
laimas,
Тут идея скрытого текста небольших сообщений обмена между "маффами", поскольку после кодирования(при отправке) и скрытия сообщения уже не прочесть(ессенно и серв не парсит кодированное) (а общение через личку - сильно много телодвижений, а тут видит конкретный чел) Обычно играют в чате - но уменя все чаты сторонние, и там тож личка - долгая песня, и не видно всех сообщений, а в форуме сразу вся страница и скрытое-невидимое остальным, меж общим кругом общения |
Вопрос: вот такая строка во что должна превращаться?
1[b]2[u]3[/b]4[/u]5 в идеале, как мне кажется, "1<b>2<u>3</u></b><u>4</u>5" по крайней мере на этом форуме именно так работает |
Цитата:
|
Цитата:
на форуме sql.ru то же самое, щас проверил |
Нет не правильно, и не с точки зрения логики, а правилами определенными.
|
:) Ну главное - идеи - идея неплоха, но нет алгоритма поиска ошибок
И проверил на больших массивах кой код, совместно с куском от devote шустрее, именно он парсит теги без проблем в один -два прохода (без всяки массивов и реверса |
:) Ну главное - идеи - идея неплоха, но нет алгоритма поиска ошибок
И проверил на больших массивах свой код, совместно с куском от devote шустрее, именно он парсит теги без проблем в один -два прохода (без всяки массивов и реверса Завтра выложу рандом - ибо лень мегабайты вставлять в песочню |
Цитата:
1) если его нет в правилах описанных, значит далее может идти текст или другой открывающий тег 2) если тег есть в правилах, проверяем что следует за ним и что может следовать за ним согласно правил, соответственно следствие нашли закрывающий тег: 1) если есть правила описанные для тега, проверяем корректно ли закрытие, и если нет, очищаем вершину стека, а текст помещаем как есть 2) если тега нет в правилах, закрытие корректное, обрамляем текст htnl-тегом, иначе ошибка и игнорируем, помещая текст как есть 3) очищаем вершину стека |
laimas,
Плюнь пока на правила для тега, это тоже легко поправимо, поставив регу от devote вместо фиксированного списка и объект с функцией перепарса по tag Воть ошибки, что принимать за корректное - а что нет - тут проблемы - ибо зависит от положения найденного тега, что за один проход вряд ли сделаешь (ну нашли, а внутри несколько вложенных валидных, или не валидных, т.е нун считать кратность вложенных идентичных - как минимум |
Цитата:
|
laimas,
Проще выложи код завтра, а я тест на скорость, ибо один фиг валидация затруднена (мну перерыл кучу, со стеком тож видел) Самый короткий и простой метод с рекурсией(идём от обрамляющих тегов к вложенным), автоматом поддерживает валидность за счёт сокращения вложенной строки - но рекурсия |
Цитата:
Кроме это, "искать возможные ошибки посредством повторных разборов строки", это накладно, гораздо проще смещаться по массиву. И хотите вы того или нет, но html это разнобразие тегов, и без правил не обойтись, вряд ли длинные портяки шаблонов рег. выражений для решения данного условия, это будет легко для RegExp. Я согласен, что простейший набор, типа как тут B, I, U, ... вполне можно обработать и RegExp, но при ошибках вложений все равно придется карячится. Стек же есть стек, если был опыт работы с ассемблером, то возможно и сталкивались с очень интересными решениями как раз используя стек, где вроде бы о нем и речи быть не должно. В РНР к примеру, в SPL, есть реализация стека. Я никого не принуждаю, как считаете нужным, так и делайте. Главная цель скорость, а остальное боком, ну так пожалуйста. Я же, для себя, подобное бы делал (буду, если потребуется) именно на стеке. |
Deff,
так что вот с этим? http://javascript.ru/forum/misc/2493...tml#post402862 с точки зрения знатока html это невалидный кейс, а для "человека обыкновенного" - просто пересечение областей. Чтобы сравнивать скорости и подходы, надо определиться с подобными случаями. |
Цитата:
Зато, как я уже говорил, есть потенциальные возможности добавить тег вроде [src] (вроде местного [js]), в которых содержится код и в которых не заменяются прочие теги. |
Цитата:
В случае вашего невалидного кода b -> u -> /b -> /u: 1) открывающий тег b в стек, далее может идти либо закрывающий тег b, любо возможный в таком наборе иной открывающий тег 2) открывающий тег u в стек, далее может идти либо закрывающий тег u, любо возможный в таком наборе иной иной открывающий тег 3) закрывающий тег b, сравниваем его с вершиной стека, а в ней u - ошибка, удаляем из вершины стека u, текст обрамляющий этим невалидным набором на вывод как есть 4) закрывающий тег u, сравниваем его с вершиной стека, а в ней b - ошибка, удаляем из вершины стека b, текст обрамляющий этим невалидным набором на вывод как есть С тегами более сложными, такими как списки, таблицы... В списках по алгоритму указанному выше будут удаляться дочерние элементы, если только в них ошибка, не закрыты к примеру, тоже самое и с таблицами, удаление некорректных ячеек. Только в этом случае нельзя бросить текст просто так между tr и td, а значит нужно анализировать где ошибка, есть ли текст предшествующий, и если есть спускаться по стеку вниз до родительского элемента table - это будет вершина стека до которой нужно выбросить некорректные вложения. |
Быстрый jS парсер BB-кодов (на "лету")
100Кб парсит менее чем за 200ms (реальные BB-коды пользователя - значительно быстрее, ибо тестировал на тегах с ошибками) <script type="text/javascript"> //Объект с функциями обработки тегов var Fn = { /*Служебные*/ none : function(tag, attr, content) {return 'µ'+tag+(attr?'='+attr:'')+']'+content+'[/'+tag+']';}, validAttr : function(attr) {return !(/[\n"<>\[\]]/.test(attr));}, parseAttr : function(attr) {return (attr? attr.replace(/^"(.*?)"$/,'$1'):'');}, attrTest : function(tag,attr) {/*Тесты Атрибутов*/}, b : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<strong>'+content+'</strong>');}, i : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<span style="font-style: italic;">'+content+'</span>');}, u : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<em class="bbuline">'+content+'</em>');}, s : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<del>'+content+'</del>');}, sup : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<sup>'+content+'</sup>');}, sub : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<sub>'+content+'</sub>');}, mark : function(tag, attr, content) {return (attr? Fn.none(tag, attr, content): '<span class="highlight-text">'+content+'</span>');}, font : function(tag, attr, content) {return '<span style="font-family: ' + attr + ';">' + content + '</span>';}, size : function(tag, attr, content) {return '<span style="font-size: ' + attr + ';">' + content + '</span>';}, color : function(tag, attr, content) {return '<span style="color: ' + attr + ';">' + content + '</span>';}, align : function(tag, attr, content) {return '<span style="display: block; text-align: ' + attr + ';">' + content + '</span>';}, url : function(tag, attr, content) {return (!attr? '<a href="' + content + '">' + content + '</a>' : '<a href="' + attr + '">' + content + '</a>');}, img : function(tag, attr, content) {return '<img src="' + content + '" class="postimg"/>';}, video : function(tag, attr, content) {return '<iframe width="480" height="284" src="'+ content +'" frameborder="0" allowfullscreen=""></iframe>';}, abbr : function(tag, attr, content) {return '<abbr title="' + attr + '">' + content + '</abbr>';}, quote : function(tag, attr, content) {return ((attr=Fn.parseAttr(attr))?'<quote title="' + attr + '">' + content + '</quote>':'<quote>' + content + '</quote>');} }; eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('Z 1j(v,s,o){6 t={},p=/[\\n¬µ]/1e,n=/\\[([^\\[\\]]*?\\[)/g,l=/(\\][^\\[\\]]*?)\\]/g,k=/\\[([^\\]]*)\\]([^\\[]*)/g,h=/(\\/)?([a-z]+)?(?:=([^\\[\\]]+))?/,f=/\\[([^\\]]{2})([^\\[\\]]+)\\]\\[\\/\\1([^\\[\\]]+)\\]/g,d=/\\[\\/?[x-z].([^\\]]+)\\]/,c=/^(.*?\\[[^\\[\\]]{2}([^\\]]+)\\])\\[\\/([^\\]]{2})([^\\[\\]]+)\\]/g,b=/\\[\\/?[x-z]./,u=/\\[[x-z][a-15-9]([a-15-9]+)\\]/g;t.o={"\\n":"<1p>","µ":"n","¬":"r;"};7(o){p=/\\n/1e}v=v.10(p,Z(i){8 t.o[i]});v=v.10(n,Z(r,i){8"µ"+i});v=v.10(l,Z(L,r,i){8 r+"¬"});t.16={};6 F=-1;t.4={};6 a=0;v="[1b]"+v;6 K,g,K,C,B,q,z,w,J;v=v.10(k,Z(L,r,i){J=1;r.10(h,Z(O,N,M,P){J=0;z=N;K=M.1k();C=P});7(J||!K||K.12>1s||z&&C){t.4[w]["R"]+="µ"+r+"¬";8""}7(!t.16[K]){B=(++F%1f+1q+19*1h(F/1f)).1i(19);t.16[K]=B}1d{B=t.16[K]}w=(a++).1i(19);t.4[w]={};t.4[w]["14"]=K;t.4[w]["18"]=C;t.4[w]["R"]=i;8("["+(z?z:"")+B+w+"]")});6 m=1m,A=v,E=0,I=A.12,j=1h(I/m)+(I%m?1:0);7(o==2){m=I;j=1}6 v="";1a(j--){v+=A.1l(E*m,m);E++;6 y=13,e=1;1a(y){y=1g;7(e==1){6 x="0",H=0,G=0;v=v.10(f,Z(Q,i,S,M,r){y=13;6 N=x,T=9+S.12+M.12;7(r>H+G+3){x=N=v.11(v.1c("[",r-1),r).10(d,"$1")}H=r;G=T;6 U=t.4[S]["14"],P=t.4[S]["18"],O=t.4[S]["R"];7(s.17&&s.17[U]){O=D(O)}7(s[U]){O=s[U](U,P,O)}1d{O=s.1b(U,P,O)}t.4[N]["R"]+=O+t.4[M]["R"];8""})}7(!y){e=2;v=v.10(c,Z(T,W,U,i,N){y=13;6 M=W.12,X=W.1c("["+i);7(X<0){t.4[U]["R"]+="µ/"+t.4[N]["14"]+"]"+t.4[N]["R"];8 W}6 r=W.11(W.1c("[",X-1),X-1),O=r.10(b,""),S=T.11(X,M),V=S.1o("]")+1,Q=S.11(V),U=S.11(3,V-1),Y=t.4[U]["14"],P=t.4[U]["18"];Q=t.4[U]["R"]+Q;7(s.17&&s.17[Y]){Q=D(Q)}7(s[Y]){Q=s[Y](Y,P,Q)}1d{Q=s.1b(Y,P,Q)}t.4[O]["R"]+=Q+t.4[N]["R"];8 T.11(0,X)})}}}Z D(L){6 i=13;6 r=/\\[[x-z][a-15-9]([a-15-9]+)\\]/g;1a(i){i=1g;L=L.10(r,Z(O,N){i=13;6 M=t.4[N]["18"];8"["+t.4[N]["14"]+(M?"="+M:"")+"]"+t.4[N]["R"]})}8 L}v=D(t.4["0"]["R"]+v.11(5));v=v.10(/[µ¬]/g,Z(i){8(i=="µ"?"[":"]")});8 v};',62,91,'||||Tgx||var|if|return|||||||||||||||||||||||||||||||||||||||||||||cntR||||||||function|replace|substring|length|true|tag|z0|Tags|ParentWrapper|attr|36|while|none|lastIndexOf|else|gim|26|false|parseInt|toString|parseBB|toLowerCase|substr|1536|181|indexOf|br|1198|172|23'.split('|'),0,{})); // Тестовая строка var text = '[quote="test3"]test [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]dtest [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]dtest [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]dtest [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]dtest [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]dtest [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]dtest [b]blah\n\ [i]tata[/i] blah[/b] param [quote="test"]d[quote="test2"]lalsd[li]fnsdf[alalsdfsda qweqweqwe [u][u][u]te[/quote]r[u]tet[/u]er[/u][/u][/u] hjf [u]sdhg[/u] en[/quote]d\ [/quote]'; st = +new Date(); var out = parseBB (text,Fn); var lng = out.length; alert('Итог: '+(+new Date()-st)+', str.length='+lng +', '+ out ); </script> В Fn = {} прописываем свои функции обработки При отсутствии тега в Fn, - в строку возвращается исходный BB-код (через Fn.none ) Если теги не валидны - так же остаются их исходные BB-коды. Функция делалась для предпросмотра "на лету" в форме ответа, при вводе символов ================== PS: Если есть интерес, - тестируем, отписываемся |
VM17787:1 Uncaught TypeError: Cannot read property 'toLowerCase' of undefined
Строка: ['URL=https://imgdepo.com/show/9759621]['IMG]http://imgdepo.com/thumb/2016/Sep/20/85264af7.jpg[/IMG'][/URL'] |
Часовой пояс GMT +3, время: 23:36. |