причем здесь юникод? если мы говорим о исполняемом коде. движек регекспов САМ написан НЕ НА ПХП. ферштейн? посимвольный разбор строки который ты собираешься сделать будет написан на ПХП. разницу чуешь? это разные уровни абстракции. соответственно разный оверхед, разные накладные расходы на хранение одного и тогоже. подумай об этом. а я пожалуй продолжу умничать. ибо знаю точно - регулярки нагнут посимвольный анализ.
Не собираюсь, надоело. Именно на PHP? И не будут вызываться нативные функции совсем? А результат регекспа ты получишь в каком виде? Что потом будешь делать с этим массивом... на "нативном" уровне опять? Единственный выигрышь тут получится, если обрабатывать действительно большой кусок строки.
Скорость будет зависеть от местерства того, кто будет писать регулярки. Скорее всего, регулярки действительно будут работать быстрее, но и регулярку можно написать тормозную Кроме того, хватит ли одной регулярки? Или это будет несколько поочерёдно вызываемых регулярок? Если хватит одной - то она должна будет сработать быстрее. Если их будет несколько - то тут уже может по-разному получиться. Применив посимвольный перебор, мы прогоняем текст через парсер всего 1 раз. А в случае нескольких регулярок - каждая регулярка - это прогон текста. Если текст в кодировке utf-8 (а это так), мы не можем в PHP использовать конструкцию Код (PHP): $char = $string[$index]; нам приходится использовать mb_substr для получения каждого символа в строке: Код (PHP): $char = mb_substr($string, $index, 1, 'utf-8'); и это одно из наиболее тормозных мест. Аналогичная конструкция в js: Код (PHP): char = string.substring(index, index + 1); Работает в несколько раз быстрее, чем в PHP. Но в принципе, если парсер будет работать только при добавлении нового комментария и записывать в б.д. уже обработанный комментарий, в котором bbcode заменён на html - то скорость работы парсера - не особо важна.
Поправьте меня если что, но кажется и vbulletin и phpbb не хранят распарсенный текст. Парсят сообщения при каждом открытии страницы. Давайте думать о быстром парсере, так, на всякий случай ) Добавлено спустя 8 минут 7 секунд: Есть правила как писать быстрые регулярки. Если упрощенно, то так, чтобы не возникало отката в разбираемой строке. В первую очередь определяемся с синтаксисом. Например, если аргумент ббкода может быть окужен кавычками, это усложняет регулярку на порядок.
Все встретили НГ? Здоровье восстановили? Продолжаем: основные моменты по синтаксису. Считаю чем проще, тем лучше. Но не проще чем требуется ) У кода может быть только один аргумент или ни одного и аргумент не может быть в кавычках. То есть тупо экранируем кавычки внутри кода! В тексте кавычки не трогаем. Html коды с амперсандом оставляем как есть, не могу как-то придумать чем они могли бы навредить. code разбирается особым образом - подавляет любые коды кроме закрывающего code. подсветку синтаксиса просто не включаем в список целей — пусть этим занимается сторонний JS. Парность требуется, но исключение есть - элемент списка. Мне кажется чтобы достичь оптимального компромиса между эффективностью и понятностью надо так: регуляркой искать лексемы, а семантику оставить на PHP.
artoodetoo, приветствую. Неужели тема заглохла? Мне тоже нужен парсер bbcode, но относительно простой, но, например, тег [size=nn] там должен быть. Просмотрел кучу разных скриптов парсеров, пока ничего не выбрал. Зато нашел скрипт парсера bb-кодов на JS, его можно на клиенте применять для предпросмотра. Если не угас интерес к теме, может, подолжим? Я здесь зарегистрировался-то только ради этой темы, по гуглу на нее вышел, случайно.
Жаль, жаль. Но я по мере сил своих хилых продолжу помалу парсером BBCode заниматься. Мне еще нужен будет обратный парсер, из bb- в html, для редактирования, т.е. в БД я все же текста храню в html (упрощенном, без кавычек). Если будет желание, взгляни на JS-скрипт скрипт неплохого парсера bb-кодов, вполне рабочего: http://blogs.stonesteps.ca/showpost.aspx?pid=33 С его помощью можно еще на клиенте, в браузере юзера предварительную валидацию текстов в bb-форматировании делать (предпросмотр и редактирование на клиенте), что разгружает сервер. А это я чуть-чуть с ним поупражнялся, для наглядности: http://sadex.p.ht/aa/sample.html Вообще в JS-скрипте этого парсера неплохая регулярка воткнута, кое-какие идеи оттуда можно взять и для PHP-парсера bb-кодов.
Кстати, помнится многих мучал вопрос с заменой рекурсивных тегов, например цитаты. Выдумывали всяких сложные регулярки, даже конечные автоматы юзали )) Пару лет назад, я кажется нашел идеальное решение... Код (PHP): <?php class BBCoder_BBList_Quote implements BBCoder_Interface { function Replace($text){ $openbb=count(preg_split("#\[quote\]#i",$text))-1; for($i=0;$i<$openbb;$i++){ $text=preg_replace("#\[quote\](.+?)\[\/quote\]#is","<div class='bbcode_quote'>\\1</div>",$text); } $text=preg_replace("#\[(\/?quote)\]#i","[\\1]",$text); return $text; } function R_Replace($text){ return preg_replace(array("#<div\sclass='bbcode_quote'>#i","#<\/div>#i"),array("[quote]","[/quote]"),$text); } } ?>
Четыре с половиной года прошло а тема для меня оказалась актуальна. Не судите строго, в сайтостроении я человек новый, - всего около месяца изучаю PHP HTML CSS JS. При том изучаю все в связке. Опыт в прикладном программировании большой, более 10 лет С++ и Delphi. Изложу свои соображения по поводу парсинга BB кода. Парсинг должен пройти на стороне клиента (дабы не грузить сервер лишней работой) Обязательно должна быть проверка на корректную вложенность тегов На стороне сервера нужно вызвать htmlspecialchars непосредственно перед записью поста в базу (дабы никто не смог внедрить ничего на страницу этого поста, JS например) Начал реализацию на JS. На данном этапе реализовал проверку логики. Код достаточно комментирован: Код (Text): // Парсим BB function ParseBB(BBstr) { // Заменяем символы конца строки на неиспользуемые юникоды BBstr.replace(/\n/g, "\u00BD"); // Выделяем все теги BB кода в массив var rg = "(\\[(?:\\s*\\u00BD*\\s*)*(B|\\/B|I|\\/I|U|\\/U|COLOR|\\/COLOR|S|\\/S|\\*|LEFT|\\/LEFT|RIGHT|\\/RIGHT|CENTER|\\/CCENTER|INDENT|\\/INDENT|EMAIL|\\/EMAIL|URL|\\/URL|THREAD|\\/THREAD|POST|\\/POST|LIST|\\/LIST|IMG|\\/IMG|VIDEO|\\/VIDEO|AUDUO|\\/AUDIO|SPOILER|\\/SPOILER|CODE|\\/CODE|QUOTE|\\QUOTE)(?:\\s*\\u00BD*\\s*)*(?:=(?:\\s*\\u00BD*\\s*)*([^\\[\\]\\s]*)|(?:\\s*\\u00BD*\\s*)*)?\\])"; rg = new RegExp(rg , "gi"); var arr = new Array(); var i = 0; while ((arr[i] = rg.exec(BBstr)) !== null) { i++; }; // Создаем стек var stk = new Array(arr.length); i = 0; for (i = 0; i < stk.length; i++) stk[i] = null; // Начинаем прогон тегов на вложенность i = 1; stk[0] = arr[0]; var idx = 0; var tmp = ""; for (i = 1; i < arr.length; i++) { // Если это последний элемент массива то выход if (arr[i] == null) break; // Если это не парный тег то следующая итерация if (arr[i][2] == "*") continue; // Если это закрывающий тег, проверяем какой тег был открыт последним if (arr[i][2][0] == "/") { if (arr[i][2].substring(1, arr[i][2].length) == stk[idx][2]) { // Если зарывающий и последний открывающий теги равны очищаем верхнюю позицию стека stk[idx] = null; idx--; // Если закрывающий тег не совпадает с последний открывающим, то ошибка в логике, выход из цикла } else break; } else { // Если это открывающий тег, то записываем его в стек idx++; stk[idx] = arr[i]; }; }; // Если вся логика верна, то стек в итоге остается пустым // если это не так, то ошибка, пишем в результат информацию о ней и выходим if (stk[0] != null) return {HTMLstr:undefined, BBteg:stk[idx][2], BBpar:stk[idx][3], Pos:stk[idx].index, Err:"Не соблюдена вложенность тегов или не закрыт тег"}; // тут будем реализовывать уже подмену тегов и втавленных ранее юникодов на HTML // Сюда запишем получившийся HTML return {HTMLstr:undefined, BBteg:undefined, BBpar:undefined, Pos:undefined, Err:undefined}; }; // Начинаем парсинг function Parse(BBstr) { var HTMLstr = ""; var res = ParseBB(BBstr); // Вывод результата или ошибки if (res.Err == undefined) { HTMLstr = res.HTMLstr; } else { HTMLstr = "Ошибочка: \"" + res.Err + "\", тег \"" + res.BBteg + "\", параметр \"" + res.BBpar + "\", позиция " + res.Pos; } return HTMLstr; } Кстати что бы было понятнее как выделяются все теги. Вводим на входе: Код (Text): [COLOR=#000000]Пр[B]осто те[/B]кст[/COLOR] Вот лог из консоли браузера на брейкпойне сразу после заполнения массива arr: Добавлено спустя 11 минут 6 секунд: То есть мы в массиве имеем выделенные: полную версию тега, его чистое название (без скобок и параметров), параметр (то что стоит после =) позицию начальной квадратной скобки данного тега в тексте
Не, на клиенте не надо. Достаточно один раз спарсить и сохранить спарсеное и нагрузки никакой, и выдача сразу красивая.
Задача трансляции BBCODE в HTML не такая простая как может показаться. Не думаю, что она станет алгоритмически проще если ее реализовать на JS вместо PHP. До сих пор я встретил только один годный парсер кроме тех что вшиты в "официальные" движки форумов. Хотя пыжатся многие — я сам начинал, но не закончил, а сейчас уже и нет желания. А как хранить результат и хранить ли вообще это вопрос отдельный.
В вашем варианте пользователь нокогда не сможет отредактировать свое же сообщение. ИМХО в базе нужно хранить непарсенный ББ код, парсить на стороне клиента при отображении Добавлено спустя 1 минуту 47 секунд: Вы правы, даже больше, учитывая особенности языка - реализация на PHP была бы проще. Добавлено спустя 2 минуты 9 секунд: Переделал некоторые моменты в логике, переделал немного регулярку поиска тегов и начал реализацию подмены ББ на HTML: Код (Text): // преобразование символов HTML в спецсимволы HTML function escapeHtml(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } // Парсим BB function ParseBB(BBstr) { // Заменяем символы конца строки на неиспользуемые юникоды BBstr = BBstr.replace(/\n/g, "\u00BD"); // Выделяем все теги BB кода в массив var rg = "\\[[\\s*\\u00BD*]*(B|\\/B|I|\\/I|U|\\/U|COLOR|\\/COLOR|S|\\/S|\\*|LEFT|\\/LEFT|RIGHT|\\/RIGHT|CENTER|\\/CCENTER|INDENT|\\/INDENT|EMAIL|\\/EMAIL|URL|\\/URL|THREAD|\\/THREAD|POST|\\/POST|LIST|\\/LIST|IMG|\\/IMG|VIDEO|\\/VIDEO|AUDUO|\\/AUDIO|SPOILER|\\/SPOILER|CODE|\\/CODE|QUOTE|\\QUOTE)[\\s*\\u00BD*]*(?:=[\\s*\\u00BD*]*([^\\[\\]\\s]*))?[\\s*\\u00BD*]*\\]"; rg = new RegExp(rg , "gi"); var arr = new Array(); var i = 0; while ((arr[i] = rg.exec(BBstr)) !== null) { i++; }; arr.pop(); if (arr.length == 0) { return {HTMLstr:escapeHtml(BBstr).replace(/\u00BD/g, "<br>"), BBteg:undefined, BBpar:undefined, Pos:undefined, Err:undefined}; } // Создаем стек var stk = new Array(); // Если первым стоит закрывающий тег то ошибочка if (arr[0][1][0] == "/") return {HTMLstr:undefined, BBteg:arr[0][1], BBpar:arr[0][2], Pos:arr[0].index, Err:"Нет предшествующего открывающего тега"}; // Если первым стоит тег элемента списка, то ошибочка if (arr[0][1] == "*") return {HTMLstr:undefined, BBteg:arr[0][1], BBpar:arr[0][2], Pos:arr[0].index, Err:"Элемент списка должен находится между открывающим и закрывающим тегами LIST"}; // Начинаем прогон тегов на вложенность stk.push(arr[0]); var tmp = ""; for (i = 1; i < arr.length; i++) { // Если это последний элемент массива то выход if (arr[i] == null) break; // Если это не парный тег то следующая итерация if (arr[i][1] == "*") continue; // Если это закрывающий тег, проверяем какой тег был открыт последним if (arr[i][1][0] == "/") { if (arr[i][1].substring(1, arr[i][1].length) == stk[stk.length - 1][1]) { // Если зарывающий и последний открывающий теги равны то передаем параметр а закрывающий тег arr[i][2] = stk[stk.length - 1][2]; // и очищаем верхнюю позицию стека stk.pop(); // Если закрывающий тег не совпадает с последний открывающим, то ошибка в логике, выход из цикла } else break; } else { // Если это открывающий тег, то записываем его в стек stk.push(arr[i]); }; }; // Если вся логика верна, то стек в итоге остается пустым // если это не так, то ошибка, пишем в результат информацию о ней и выходим if (stk.length > 0) return {HTMLstr:undefined, BBteg:stk[stk.length - 1][2], BBpar:stk[stk.length - 1][3], Pos:stk[stk.length - 1].index, Err:"Не соблюдена вложенность тегов или не закрыт тег"}; // тут будем реализовывать уже подмену тегов и втавленных ранее юникодов на HTML // Заносим начало строки до первого тега в результат var HTMLstr = {HTMLstr:"", BBteg:undefined, BBpar:undefined, Pos:undefined, Err:undefined}; var l; if (arr.length == 0) l = BBstr.length else l = arr[0].index; HTMLstr.HTMLstr = escapeHtml(BBstr.substring(0, l)).replace(/\u00BD/g, "<br>"); // Перебираем массив тегов, смотрим их позиции в строке и втыкаем соответствующие теги while (arr.length > 0) { if (arr.length == 1) l = BBstr.length else l = arr[1].index; switch (arr[0][1].toUpperCase()) { case "CODE": break; case "*": // Проверяем наличие открытого тега [LIST] break; case "B": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<b>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/B": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</b>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "I": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<i>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/I": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</i>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "U": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<u>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/U": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</u>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "S": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<strike>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/S": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</strike>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "COLOR": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<span style=\"color:" + arr[0][2] + "\">" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/COLOR": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</span>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "URL": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<a href=\"" + arr[0][2] + "\">" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/URL": if (arr[0]) HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</a>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "IMG": if (arr[0][2] != undefined) HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<img src=\"" + arr[0][2] + "\" alt=\"" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>") + "\"/>"; else HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<img src=\"" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>") + "\"/>"; break; case "/IMG": HTMLstr.HTMLstr = HTMLstr.HTMLstr + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "LEFT": break; case "/LEFT": break; case "RIGHT": break; case "/RIGHT": break; case "CENTER": break; case "/CENTER": break; case "INDENT": break; case "/INDENT": break; case "EMAIL": break; case "/EMAIL": break; case "THREAD": break; case "/THREAD": break; case "POST": break; case "/POST": break; case "LIST": break; case "/LIST": break; case "VIDEO": break; case "/VIDEO": break; case "AUDIO": break; case "/AUDIO": break; case "QUOTE": break; case "/QUOTE": break; case "SPOILER": break; case "/SPOILER": break; } arr.shift(); }; // Сюда запишем получившийся HTML return HTMLstr }; function Parse(BBstr) { var HTMLstr = ""; var res = ParseBB(BBstr); if (res.Err == undefined) { HTMLstr = res.HTMLstr; } else { HTMLstr = "Ошибочка: \"" + res.Err + "\", тег \"" + res.BBteg + "\", параметр \"" + res.BBpar + "\", позиция " + res.Pos; } return HTMLstr; } Добавлено спустя 12 минут 34 секунды: Сегодня вечером сделаю поправку в логику вложенности на тег CODE, ибо внутри него может быть все что угодно кроме неэкранированного закрывающего тега /CODE. Потом продолжу забивать подмену ББ на HTML.
igordata, в данном случае дело личное каждого на чьей стороне реализовывать парсинг. Вы считаете на стороне сервера, я считаю в целях безопасности и стабильности на обеих сторонах. Штатный режим работы: парсинг работает на стороне клиента и сервера не касается, на сервер летит POST только когда пользователь подготовил, отредактировал и 100 раз посмотрел свой пост в предварительном просмотре сервер прогоняет парсинг заново (никто же не мешает злоумышленнику отсылать запрос серверу не с вашей страницы или вообще не из браузера) сервер пишет 2 варианта в базу (BB и HTML) при запросах на отображение страницы сервер выдает HTML, при запросе на редактирование своего поста от пользователя - BB Ну думаю харОшь теории... Вот что у меня получается. Оформление тегов BB кода: название тега парсится регистронезависимо, т.е. так будет правильно COLOR, color, cOlOr между открывающей скобкой и названием тега может быть сколько угодно пробелов, табуляций и переносов строки между названием тега и закрывающей скобкой может быть сколько угодно пробелов, табуляций и переносов строки если присутствуют атрибуты тега то между названием тега и знаком равно может быть сколько угодно пробелов, табуляций и переносов строки между знаком равно и атрибутом может быть сколько угодно пробелов, табуляций и переносов строки между атрибутом и закрывающей скобкой может быть сколько угодно пробелов, табуляций и переносов строки в закрывающем теге между слешем и названием тега не должно быть пробелов, слеш как таковой входит в название тега Правильные примеры: Код (Text): [ b ]Текст[ /b ] [ img = Текст ] Текст[ /img ] Неправильный пример: Код (Text): [b]Текст[/ b] На данный момент логическая проверка реализована полностью. Проверяется правильная вложенность тегов, обязательное закрытие тегов (кроме пункта списка *). Если встречается пункт списка то проверяется, находится ли он внутри открывающего и закрывающего тега LIST Если встречается тег CODE то все дальнейшие теги просто игнорируются и пишутся как текст до тех пор пока не встретится закрывающий /CODE. Если в тексте кода необходимо вставить в виде текста закрывающий /CODE то после начальной скобкой экранируем его обратным слешем \CODE. Погнали по тегам: Код (Text): [B]Жирный[/B] [U]Подчеркнутый[/U] [I]Курсив[/I] [S]Зачеркнутый[/S] [COLOR=#00FF00]Текст для выделения цветом[/COLOR] [URL]http://nasgool.ru[/URL] [URL=http://nasgool.ru]Ссылка на мой сайт[/URL] [IMG]Ссылка на картинку[/IMG] [IMG=Ссылка на картинку]Альт текст если в браузере отключены картинки[/IMG] В коде написано преобразование каких тегов еще необходимо реализовать: Код (Text): // преобразование символов HTML в спецсимволы HTML function escapeHtml(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } // Парсим BB function ParseBB(BBstr) { // Заменяем символы конца строки на неиспользуемые юникоды BBstr = BBstr.replace(/\n/g, "\u00BD"); // Выделяем все теги BB кода в массив var rg = "\\[[\\s*\\u00BD*]*(B|\\/B|I|\\/I|U|\\/U|COLOR|\\/COLOR|S|\\/S|\\*|LEFT|\\/LEFT|RIGHT|\\/RIGHT|CENTER|\\/CCENTER|INDENT|\\/INDENT|EMAIL|\\/EMAIL|URL|\\/URL|THREAD|\\/THREAD|POST|\\/POST|LIST|\\/LIST|IMG|\\/IMG|VIDEO|\\/VIDEO|AUDUO|\\/AUDIO|SPOILER|\\/SPOILER|CODE|\\/CODE|QUOTE|\\QUOTE)[\\s*\\u00BD*]*(?:=[\\s*\\u00BD*]*([^\\[\\]\\s]*))?[\\s*\\u00BD*]*\\]"; rg = new RegExp(rg , "gi"); var arr = new Array(); var i = 0; while ((arr[i] = rg.exec(BBstr)) !== null) { i++; }; arr.pop(); if (arr.length == 0) { return {HTMLstr:escapeHtml(BBstr).replace(/\u00BD/g, "<br>"), BBteg:undefined, BBpar:undefined, Pos:undefined, Err:undefined}; } // Создаем стек var stk = new Array(); // Если первым стоит закрывающий тег то ошибочка if (arr[0][1][0] == "/") return {HTMLstr:undefined, BBteg:arr[0][1], BBpar:arr[0][2], Pos:arr[0].index, Err:"Нет предшествующего открывающего тега"}; // Если первым стоит закрывающий тег то ошибочка if (arr[0][1] == "*") return {HTMLstr:undefined, BBteg:arr[0][1], BBpar:arr[0][2], Pos:arr[0].index, Err:"Элемент списка должен находится между открывающи и закрывающим тегами LIST"}; // Начинаем прогон тегов на вложенность stk.push(arr[0]); var cde = (stk[0][1].toUpperCase() == "CODE"); var j; var cl; var vl; for (i = 1; i < arr.length; i++) { // Проверяем открыт ли тег CODE if (cde) { // Если код открыт то помечаем теги на удаление, до закрытия тега if (arr[i][1].toUpperCase() != "/CODE") { arr[i][1] = "del"; continue; } else { cde = false; stk.pop(); continue; }; }; // Если это тег элемента списка то проверяем, есть ли открытый тег LIST if (arr[i][1] == "*") { vl = false; cl = false; for (j = i - 1; j > -1; j--) { if (arr[j][1].toUpperCase() == "/LIST"){ cl = true; continue; }; if (cl && (arr[j][1] == "LIST")) { cl = false; continue; }; if (!cl && (arr[j][1] == "LIST")) { vl = true; break; }; }; if (!vl) return {HTMLstr:undefined, BBteg:arr[i][1], BBpar:arr[i][2], Pos:arr[i].index, Err:"Элемент списка должен находится между открывающи и закрывающим тегами LIST"}; }; // Если это закрывающий тег, проверяем какой тег был открыт последним if (arr[i][1][0] == "/") { if (arr[i][1].substring(1, arr[i][1].length) == stk[stk.length - 1][1]) { // Если зарывающий и последний открывающий теги равны то передаем параметр а закрывающий тег arr[i][2] = stk[stk.length - 1][2]; // и очищаем верхнюю позицию стека stk.pop(); // Если закрывающий тег не совпадает с последний открывающим, то ошибка в логике, выход из цикла } else break; } else { // Если это открывающий тег, то записываем его в стек stk.push(arr[i]); }; }; // Удаляем помеченные теги for (i = arr.length - 1; i > -1; i--) if (arr[i][1] == "del") arr.splice(i, 1); // Если вся логика верна, то стек в итоге остается пустым // если это не так, то ошибка, пишем в результат информацию о ней и выходим if (stk.length > 0) return {HTMLstr:undefined, BBteg:stk[stk.length - 1][2], BBpar:stk[stk.length - 1][3], Pos:stk[stk.length - 1].index, Err:"Не соблюдена вложенность тегов или не закрыт тег"}; // тут будем реализовывать уже подмену тегов и втавленных ранее юникодов на HTML // Заносим начало строки до первого тега в результат var HTMLstr = {HTMLstr:"", BBteg:undefined, BBpar:undefined, Pos:undefined, Err:undefined}; var l; if (arr.length == 0) l = BBstr.length else l = arr[0].index; HTMLstr.HTMLstr = escapeHtml(BBstr.substring(0, l)).replace(/\u00BD/g, "<br>"); // Перебираем массив тегов, смотрим их позиции в строке и втыкаем соответствующие теги while (arr.length > 0) { if (arr.length == 1) l = BBstr.length else l = arr[1].index; switch (arr[0][1].toUpperCase()) { case "CODE": break; case "*": break; case "B": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<b>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/B": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</b>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "I": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<i>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/I": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</i>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "U": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<u>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/U": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</u>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "S": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<strike>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/S": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</strike>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "COLOR": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<span style=\"color:" + arr[0][2] + "\">" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/COLOR": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</span>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "URL": HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<a href=\"" + arr[0][2] + "\">" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "/URL": if (arr[0]) HTMLstr.HTMLstr = HTMLstr.HTMLstr + "</a>" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "IMG": if (arr[0][2] != undefined) HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<img src=\"" + arr[0][2] + "\" alt=\"" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>") + "\"/>"; else HTMLstr.HTMLstr = HTMLstr.HTMLstr + "<img src=\"" + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>") + "\"/>"; break; case "/IMG": HTMLstr.HTMLstr = HTMLstr.HTMLstr + escapeHtml(BBstr.substring(arr[0].index + arr[0][0].length, l)).replace(/\u00BD/g, "<br>"); break; case "LEFT": break; case "/LEFT": break; case "RIGHT": break; case "/RIGHT": break; case "CENTER": break; case "/CENTER": break; case "INDENT": break; case "/INDENT": break; case "EMAIL": break; case "/EMAIL": break; case "THREAD": break; case "/THREAD": break; case "POST": break; case "/POST": break; case "LIST": break; case "/LIST": break; case "VIDEO": break; case "/VIDEO": break; case "AUDIO": break; case "/AUDIO": break; case "QUOTE": break; case "/QUOTE": break; case "SPOILER": break; case "/SPOILER": break; } arr.shift(); }; // Сюда запишем получившийся HTML return HTMLstr }; function Parse(BBstr) { var HTMLstr = ""; var res = ParseBB(BBstr); if (res.Err == undefined) { HTMLstr = res.HTMLstr; } else { HTMLstr = "Ошибочка: \"" + res.Err + "\", тег \"" + res.BBteg + "\", параметр \"" + res.BBpar + "\", позиция " + res.Pos; } return HTMLstr; }
ммм, нет. Это не дело и не личное. Это вопрос того, что вы хотите дать своим посетителям. Даже если ваша раскраска будет быстрой, один хрен это скажется на рассчёте высоты страницы, что приведёт к дерганью. Так что на мой взгляд это даже не вопрос, а так, предположение, что возможно когда-то, можно будет реализовать парсинг на клиенте.
Не буду отвлекаться от практики на ненужную теорию. Вы бы конкретнее, с примерами. Аргументируйте. ЗЫ Про подергивания... Мне ничто не мешает показывать загрузочную анимацию и просто надпись "Жди... работаю..." вместо контента, пока идет парсинг. ЗЗЫ А вобще, вы про верстку а я про кодинг )
это всё плохо сказывается на поведении посетителей. Поэтому не не вопрос мнений, и не теория. Если вы пилите нечто, что нафик никому не нужно или даже вредит тем, кто будет этим пользоваться - вот это ваше личное дело. Но вы должны это осознавать. =)
Незнаю работает ли оно на последних версиях PHP (поидее должно), но я просто оставлю это здесь: http://pecl.php.net/package/bbcode
скажу коротко - нерабочее говно некто Чушкин уже предлагал его (также не читая и не пробуя) ещё на первой странице.
Жаль что времени на свои заморочки мало, работа все съедает... Код (Text): [B]Жирный[/B] [U]Подчеркнутый[/U] [I]Курсив[/I] [S]Зачеркнутый[/S] [COLOR=#00FF00]Текст для выделения цветом[/COLOR] [IMG]Ссылка на картинку[/IMG] [IMG=Ссылка на картинку]Альт текст если в браузере отключены картинки[/IMG] [URL]Ссылка[/URL] [URL=Ссылка]Видимый текст[/URL] [EMAIL]Почта[/EMAIL] [EMAIL=Почта]Видимый текст[/EMAIL] [CODE]Код[ / CODE] [SPOILER=Заголовок спойлера] Скрытый спойлером текст или другие теги[/SPOILER] [LIST] [*]Первая запись маркированного списка [*]Вторая запись маркированного списка [/LIST] [LIST=A] [*]Первая запись нумерованного списка [*]Вторая запись нумерованного списка [*]Значения атрибута 1, A, a, I, i (любой другой атрибут расценивается как 1) [/LIST] [QUOTE=Заголовок цитаты]Текст цитаты[/QUOTE] [VIDEO]Ссылка на страницу видео[/VIDEO] [AUDIO]Прямая ссылка на файл или поток[/AUDIO] Поправил ошибки в регулярке. Рализовал оставшиеся теги. Видео пока работает только с ютюба. В планах добавить разных сервисов к видео (определять будет по атрибуту например VIDEO=YOUTUBE) Так же хочу прикрутить атрибут к CODE=PHP/JS/HTML/C/DELPHI и делать соответствующую подсветку кода https://yadi.sk/d/5TVXPR6if3azw ссылка на код и css (без него не работает спойлер и плохое оформление), если есть конструктивные идеи я весь внимание. Добавлено спустя 12 минут 42 секунды: Забыл добавить о возвращаемом объекте функцией ParseBB() {HTMLstr:string, // HTML полученный в результате парсинга, если были ошибки то undefined BBteg:string, // undefined, если случилась ошибка, тут название тега в котором это произошло BBpar:string, // undefined, если случилась ошибка, тут атрибут тега в котором это произошло, если нет атрибута то undefined Pos:int, // undefined, если случилась ошибка, тут позиция тега в исходной строке в котором это произошло Err:string} // undefined, если случилась ошибка, тут сообщение, которое разъясняет ошибку
Прикрутил я подсветку кода от google. Ссылка тут и в предыдущем посте новая. Для его работы необходимо скачать стиль: https://code.google.com/p/google-code-prettify/source/browse/trunk/src/prettify.css Прописать его на страницу: Код (Text): <link href="css/prettify.css" rel="stylesheet"> Туда же нужно прописать загрузчик скрипта: Код (Text): <script src="https://google-code-prettify.googlecode.com/svn/loader/prettify.js"></script> Далее нужно поправить onload станицы и onclick кнопки предварительного просмотра, добавив туда функцию: Код (Text): prettyPrint(); Для того что бы нумерация строк кода была на каждой строке а не каждые 5 строк, нужно в prettify.css добавить: Код (Text): li.L0, li.L1, li.L2, li.L3, li.L5, li.L6, li.L7, li.L8 { list-style-type: decimal !important } Скачать скрипт и стиль: https://yadi.sk/d/5TVXPR6if3azw На этом я думаю все с этой темой.