RegEx: обнаружить интерполяцию строки, но не внутри атрибута

Я работаю над созданием веб-компонентов, и мне нужно регулярное выражение, которое фиксирует случаи интерполяции строк в строке шаблона. Например, со следующей строкой:

<img src = "${this.image}"/><h5>${this.title}</h5><p>${this.description}</p>

Случаи интерполяции строк находятся внутри ${} и могут быть захвачены с помощью: (this(\.\w+)).
Но я не хочу захватывать первый экземпляр, потому что он находится внутри атрибута.

Я попробовал выражение ((?<!".+)this(\.\w+)+(?!.+")), которое работает с многострочной строкой (каждый тег в отдельной строке), но теперь с одной строкой.

Вот моя демо RegExr.
Возможно, кто-то с большим опытом в RegEx может мне помочь.

Редактировать

Чтобы вопрос был простым и по существу, я не упомянул об этом...

Причина, по которой я хочу это сделать, заключается в том, что я использую Lit для создания веб-компонентов, я уже создал функцию интерполятора, которая возвращает Lit TemplateResult , теперь я хочу выделить данные с помощью тегов <b>, поэтому я хочу замените совпадения RexEx директивой unsafeHTML, но unsafeHTML выдает ошибку внутри атрибутов.
Вот моя функция интерполятора:

export function FillTemplate(templateString: string, data: any): TemplateResult {
    let regex = /((?<!".+)this(\.\w+)+(?!.+"))/g;
    if (regex.test(templateString)) {
        templateString = templateString.replace(/((?<!".+)this(\.\w+)+(?!.+"))/g, "unsafeHTML($1)");
    }
    return new Function('html', 'unsafeHTML', "return html`"+templateString +"`;").call(data, html, unsafeHTML);
};

.... Я также подумаю об этом, может быть, мне лучше проверить ключи объекта, а не строку шаблона...

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
57
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я думаю, что это должно сработать для вас:

[^"]\$\{(this\.\w+)

Для этого потребуются только интерполяции, которым не предшествует "

Используйте следующее регулярное выражение:

[^ = "]{2}\${(\S+?)}

  1. Атрибуты всегда будут иметь = и их значение будет в кавычках. Таким образом, [^ = "]{2} гарантирует, что мы сопоставляем два символа, которые не являются = и ".
  2. (\S+?) затем лениво захватывает необходимые данные в группу захвата.

Демо

[^=][^"] будет точнее, чем [^ = "]{2}, и исключить ==, например "=
Peter Thoeny 20.12.2022 21:58
Ответ принят как подходящий

Вы можете использовать отрицательный просмотр назад для учета атрибута в кавычках: ?<!=["'])\$\{this(?:\.\w+)+\}. Это исключит src = "${this.image}" в вашем примере, но вы получите ложное срабатывание для текста HTML, например <p>Quote: "${this.quote}"</p>

Вы можете использовать отрицательный просмотр назад для учета атрибута в кавычках в теге HTML: (?<!<\w+ (\w+=["'][^"']*["'] )*\w+=["'])\$\{this(?:\.\w+)+\}.

Вот пример с обоими регулярными выражениями:

const regex1 = /(?<!["'])\$\{this(?:\.\w+)+\}/g;
const regex2 = /(?<!<\w+ (\w+=["'][^"']*["'] )*\w+=["'])\$\{this(?:\.\w+)+\}/g;

[
  '<img src = "${this.image}"/><h5>${this.title}</h5><p>${this.description}</p><p>Quote: "${this.quote}"</p>',
  '<img foo = "bar" src = "${this.image}"/><h5>${this.title}</h5><p>${this.description}</p><p>Quote: "${this.quote}"</p>'
].forEach(str => {
  console.info(str);
  console.info('- regex1:', str.match(regex1));
  console.info('- regex2:', str.match(regex2));
});

Объяснение regex2:

  • (?<! -- отрицательный просмотр назад
  • <\w+ -- начало тега HTML и пробел <img
  • (\w+=["'][^"']*["'] )* -- 0+ атрибутов формы attr = "value" с пробелом в конце
  • \w+=["'] -- начало атрибута, например src = " или src='
  • ) -- отрицательный просмотр назад
  • \$\{this -- буквально ${this
  • (?:\.\w+)+ -- группа без захвата для 1+ паттернов .something
  • \} -- буквально }

Примечание. Если ваш движок регулярных выражений не поддерживает отрицательный просмотр назад (особенно Safari), вы можете изменить его на группу захвата и восстановить ее с помощью .replace()

Я собираюсь принять это как ответ, поскольку он является наиболее полным и объясняет выражение. С Рождеством!

Luke T O'Brien 23.12.2022 12:16

Этот также будет учитывать атрибуты (вопреки тому, что спрашивали).
Альтернативное решение вместо регулярного выражения (и если вы доверяете data) будет использовать конструктор функций и позволить синтаксическому анализатору JavaScript интерпретировать и оценивать строку как литерал шаблона и выполнять за вас желаемую работу:

const interpolate = (str, data) =>
  Function("return (`" + str + "`);").call(data);


// Use like:
const str = '<img src = "${this.image}"/><h5>${this.title}</h5><p>${this.description}</p>';

const data = {
  title: "Lorem ipsum",
  description: "Dolor sit amet",
  image: "https://i.stack.imgur.com/zH7ZS.jpg?s=64&g=1",
};

document.body.insertAdjacentHTML("beforeend", interpolate(str, data));

Кроме того, если вы обнаружите, что this в вашем шаблоне слишком повторяется, вы можете напрямую использовать ключи объекта и .apply() значения, как в этом решении:

const interpolate = (str, data) =>
  Function(...Object.keys(data), "return (`" + str + "`);").apply(null, Object.values(data));


// Use like:
const str = '<img src = "${image}"/><h5>${title}</h5><p>${description}</p>';

const data = {
  title: "Lorem ipsum",
  description: "Dolor sit amet",
  image: "https://i.stack.imgur.com/zH7ZS.jpg?s=64&g=1",
};

document.body.insertAdjacentHTML("beforeend", interpolate(str, data));

или, как указано выше (без this, с использованием ключей объекта) без небезопасной оценки, было бы с использованием String.prototype.replace() и регулярного выражения, такого как /\$\{([^}]+)\}/g:

const interpolate = (str, data) =>
  str.replace(/\$\{([^}]+)\}/g, (_, k) => data[k]);

const str = '<img src = "${image}"/><h5>${title}</h5><p>${description}</p>';
const data = {
  title: "Lorem ipsum",
  description: "Dolor sit amet",
  image: "https://i.stack.imgur.com/zH7ZS.jpg?s=64&g=1",
};

document.body.insertAdjacentHTML("beforeend", interpolate(str, data));

+1 за использование моего изображения и за функцию interpolate. Однако я создал функцию, которая возвращает LitElement TemplateResult... Я обновлю свой вопрос, чтобы показать свою функцию

Luke T O'Brien 21.12.2022 23:10

Другие вопросы по теме