2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 BBCode и "умный" перебор
Сообщение25.05.2021, 15:27 


21/05/16
4292
Аделаида
Возникла весьма интересная задача.
Я сейчас пишу BBCode-парсер, который, по сути, конвертирует обычный текст с BBCode в HTML. Допустим, пользователь выделяет некоторую часть конвертированного сообщения и хочет её процитировать. При этом надо, чтобы выделенный им HTML преобразовался обратно в BBCode.
Есть очевидное решение - взять исходный текст сообщения, перебрать все его подстроки, каждую отббкодить, и проверить, совпадает ли с выделенной пользователем. Но, очевидно, это весьма неэффективно.
Есть ли способы сократить такой поиск? У меня есть идея искать сначала по просто чистому тексту из HTML (т.е. выбросив теги), но я не совсем уверен, как её можно реализовать.
Если что, то вот код моего парсера BBCode:
код: [ скачать ] [ спрятать ]
Используется синтаксис Javascript
function repeatWhileDifferent(text, func) {
    let oldText;
    do
        oldText = text;
    while ((text = func(text)) != oldText);
    return text;
}

function applyBBCode(text) {
    let map = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '\n': '<br>', '\x01': '\x03', '\x02': '\x03'};
    let lastSpoilerID = 0;
    let noCode = [];
    let regExpList = [
        [/\[nocode\](.*?)\[\/nocode\]/isg,                                            (s, p) => '\x01' + noCode.push(p) + '\x02'],
        [/\[(b|i|u|s|left|center|right|code)\](.+?)\[\/\1\]/isg,                      "<span class='text_$1'>$2</span>"],
        [/\[url=['\"]*(http\:\/\/.+?|https\:\/\/.+?|\/.*?)['\"]*\](.+?)\[\/url\]/isg, (s, p1, p2) => `<a href='${eqsc(p1)}' rel='nofollow' target='_blank'>${p2}</a>`],
        [/\[url\](http\:\/\/.+?|https\:\/\/.+?|\/.*?)\[\/url\]/isg,                   (s, p1) => `<a href='${eqsc(p1)}' rel='nofollow' target='_blank'>${p1}</a>`],
        [/\[img\](http\:\/\/.+?|https\:\/\/.+?|\/.*?)\[\/img\]/isg,                   (s, p1) => `<img src='${eqsc(p1)}' alt='Изображение'>`],
        [/\[spoiler=(.+)](.+?)\[\/spoiler\]/isg,                                      (s, p1, p2) => {
            let id = ++window.bbCode_lastSpoilerID;
            return `<a href='' onclick='return toggleSpoiler("
${id}");'>${p1}</a><div id='${id}' style='display: none;'>${p2}</div>`;
        }],
        [/\[font=([^'\"]+)\](.+?)\[\/font\]/isg,                                      `<span style='font-family: "
$1", serif;'>$2</span>`],
        [/\[size=([1-9]|[1-9]\d|[1-4]\d\d)\](.+?)\[\/size\]/isg,                      "
<span style='font-size: $1%;'>$2</span>"],
        [/\[color=([a-zA-Z]+|\#?[0-9a-fA-F]{6})\](.+?)\[\/color\]/isg,                "
<span style='color: $1;'>$2</span>"],
        [/\[youtube\](.+?)\[\/youtube\]/isg,                                          (s, p) => {
            let lastAmpersand = p.length - 1;
            while ((lastAmpersand >= 0) && (p[lastAmpersand] != '&'))
                lastAmpersand--;
            if ((lastAmpersand >= 0) && p.slice(lastAmpersand).startsWith('&amp;t='))
                p = p.slice(0, lastAmpersand);
            let id = p.slice(-11);
            if (!/^[a-zA-Z0-9]+$/.test(id))
                return "
";
            else
                return `<iframe allowfullscreen allow='fullscreen; picture-in-picture' title='YouTube video player' width='640' height='385' src='https://youtube.com/embed/${id}'></iframe>`;
        }],
        [/\[table\](.+?)\[\/table\]/isg,                                              "
<table class='normal_table'>$1</table>"],
        [/\[table=0\](.+?)\[\/table\]/isg,                                            "
<table>$1</table>"],
        [/\[(tr|td)\](.*?)\[\/\1\]/isg,                                               "
<$1>$2</$1>"],
        [/\x01(\d+)\x02/g,                                                            (s, num) => noCode[num - 1]]
    ];
    HTML = text.replace(/[&<>\n]/g, s => map[s]);
    for (let regexp of regExpList)
        HTML = repeatWhileDifferent(HTML, (str) => str.replace(regexp[0], regexp[1]));
    return HTML;
}

function eqsc(str) {
    let quotesSemiColonMap = {"
'": '&apos;', '"': '&quot;', ';': '&semi;', ':': '&colon;'};
    return str.replace(/['\";:]/g, s => quotesSemiColonMap[s]);
}

function toggleSpoiler(id) {
    let element = document.getElementById(id);
    element.style.display = (element.style.display == 'block') ? 'none' : 'block';
    return false;
}


-- 25 май 2021, 22:14 --

Ещё, кстати, может быть проблема с частичным выделением - если изначально было сообщение [b]abc[/b] [b]def[/b], то оно станет <span class='text_b'>abc</span> <span class='text_b'>def</span>, и если пользователь захочет выделить строку bc de, он выделит HTML <span class='text_b'>bc</span> <span class='text_b'>de</span>, и это должно будет стать [b]bc[/b] [b]de[/b], несмотря на то, что в исходном тексте такой строки нет.

-- 25 май 2021, 22:39 --

В принципе, самый простой способ - просто сделать анти-BBCode, т.е. превращения HTML-тегов обратно в BBCode.

 Профиль  
                  
 
 Re: BBCode и "умный" перебор
Сообщение26.05.2021, 02:03 
Заслуженный участник


26/05/14
981
В процессе перевода из BBCode в HTML запоминайте какой по номеру символ из BBCode отображается в какой по номеру символ в HTML. Получившееся отображение - монотонная функция. Обратная функция вычисляется за логарифм двоичным поиском. Тут срезаны некоторые углы (эти функции не для всех номеров определены), но должно работать.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 2 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group