Regex, исключая совпадения, заключенные в определенные теги bbcode - PullRequest
0 голосов
/ 02 марта 2019

Я пытаюсь заменить двойные кавычки фигурными кавычками, за исключением случаев, когда текст обернут в определенные теги, такие как [quote] и [code].

Пример ввода

[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>

Ожидаемый результат

[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>

Я понял, как элегантно достичь того, что я хочу в PHP, используя (*SKIP)(*F), однако мой код будет выполняться в javascript,и решение javascript далеко не идеальное.

Прямо сейчас я разбиваю строку на эти теги, запускаю замену, затем соединяю строку:

var o = 3;
a = a
    .split(/(\[(?<first>(?:icode|quote|code))[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(?:\k<first>)\])/i)
    .map(function(x,i) {
        if (i == o-1 && x) {
            x = '';
        }
        else if (i == o && x)
        {
            x = x.replace(/(?![^<]*>|[^\[]*\])"([^"]*?)"/gi, '“$1”')
            o = o+3;
        }
        return x;
    }).join('');

JavascriptРаспределение регулярных выражений

  1. Внутри split():
    • (\[(?<first>icode|quote|code)[^\]]*?\](?:.)*?\[\/(\k<first>)\]) - фиксирует шаблон в скобках:
      • \[(?<first>quote|code|icode)[^\]]*?\] - [quote],[code] или [icode] открывающий тег с такими параметрами или без них, как =html, например, [code=html]
      • (?:[\s]*?.)*? - любые 0+ (как можно меньше) вхождений любого символа (* 1039)*), перед или без пробела, поэтому он не прерывается, если за открывающим тегом следует разрыв строки
      • [\s]*? - 0+ пробелаs
      • \[\/(\k<first>)\] - [\quote], [\code] или [\icode] закрывающие теги.Соответствует тексту, захваченному в группе (?<first>).Например: если это цитата открывающий тег, это будет цитата закрывающий тег
  2. Внутри replace():
    • (?![^<]*>|[^\[]*\])"([^"]*?)" - захватывает текст в двойных кавычках:
      • (?![^<]*>|[^\[]*\]) - отрицательный прогноз, ищет символы (которые не являются < или [), за которыми следует либо> или ] и отбрасывает их, поэтому ничего не будет совпадать с внутри тегов bbcode и html.Например: [spoiler="Name"] или <span style="color: #24c4f9">.Обратите внимание, что совпадения в тегах в тегах остаются без изменений.
      • " - буквально открывающийся символ двойных кавычек.
      • ([^"]*?) - любой символ 0+, кроме двойных кавычек.
      • " - буквальный символ двойных кавычек.

SPLIT () REGEX DEMO: https://regex101.com/r/Ugy3GG/1

Это ужасно, потому что замена выполняется несколько раз.


Между тем, тот же результат может быть достигнут с помощью одного регулярного выражения PHP.Регулярное выражение, которое я написал, было основано на шаблоне соответствия регулярному выражению, которого нет в теге bbcode .

(\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F)|(?![^<]*>|[^\[]*\])"([^"]*?)"

Разбивка PHP Regex

  • (\[(?<first>quote|code|icode)[^\]]*?\](?:[\s]*?.)*?[\s]*?\[\/(\k<first>)\])(*SKIP)(*F) - совпадает с шаблоном внутри круглых скобок, как javascript split() выше, затем (*SKIP)(*F) заставляет механизм регулярных выражений опускать сопоставленный текст.
  • | - или
  • (?![^<]*>|[^\[]*\])"([^"]*?)" - захватывает текст внутри двойных кавычек таким же образом, как javascript replace() делает

PHP DEMO: https://regex101.com/r/fB0lyI/1

Красота этого регулярного выраженияв том, что его нужно запускать только один раз.Нет разделения и объединения строк.Есть ли способ реализовать это в javascript?

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Поскольку в JS отсутствуют глаголы обратного хода, вам нужно будет использовать эти блоки в скобках, но позже заменить их как есть.Получив вторую часть чередования из вашего собственного регулярного выражения, можно получить конечное регулярное выражение:

\[(quote|i?code)[^\]]*\][\s\S]*?\[\/\1\]|(?![^<]*>|[^\[]*\])"([^"]*)"

Но сложная часть использует функцию обратного вызова с методом replace():

str.replace(regex, function($0, $1, $2) {
    return $1 ? $0 : '“'  + $2 + '”';
})

Выше троичного оператора возвращает $0 (полное совпадение), если существует первая группа захвата, в противном случае он заключает вторую фигурную группу захвата в фигурные кавычки и возвращает ее.

Примечание: это может не сработать в разных случаях.

См. демо здесь

0 голосов
/ 02 марта 2019

Вложенную разметку сложно проанализировать с помощью rx, в частности, RegExp в JS.Сложные регулярные выражения также трудно читать, поддерживать и отлаживать.Если ваши потребности просты, замена содержимого тега исключена с некоторыми запрещенными тегами, рассмотрите простую альтернативу на основе кода для запуска RegExps:

function curly(str) {
    var excludes = {
        quote: 1,
        code: 1,
        icode: 1
    },
    xpath = [];

    return str.split(/(\[[^\]]+\])/) // breakup by tag markup
        .map(x => { // for each tag and content:
            if (x[0] === "[") { // tag markup:
                if (x[1] === "/") { // close tag
                    xpath.pop(); // remove from current path
                } else { // open tag
                    xpath.push(x.slice(1).split(/\W/)[0]); // add to current path
                } //end if open/close tag
            } else { // tag content
                if (xpath.every(tag =>!excludes[tag])) x = x.replace(/"/g, function repr() {
                    return (repr.z = !repr.z) ? "“" : "”"; // flip flop return value (naive)
                });
            } //end if markup or content?
            return x;
        }) // end term map
        .join("");
} /* end curly() */

var input = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>"Why no goodbye?" replied [b]Bob[/b]. "It's always Hello!"</p>`;

var wants = `[quote="Name"][b]Alice[/b] said, "Hello world!"[/quote]
<p>“Why no goodbye?” replied [b]Bob[/b]. “It's always Hello!”</p>`;

curly(input) == wants; // true

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

Если ваши потребности более сложны, используйте настоящий анализатор BBCode для JavaScript и отобразите / отфильтруйте / уменьшите его модельпо мере необходимости.

...