Создавайте RegExps на лету, используя строковые переменные

138

Скажем, я хотел сделать следующее многоразовым:

function replace_foo(target, replacement) {
   return target.replace("string_to_replace",replacement);
}

Я мог бы сделать что-то вроде этого:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(string_to_replace,replacement);
}

Со строковыми литералами это достаточно просто. Но что, если я хочу немного усложнить регулярное выражение? Например, сказать, что я хочу заменить все, кроме string_to_replace . Инстинктивно я бы попытался расширить вышесказанное, выполнив что-то вроде:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(/^string_to_replace/,replacement);
}

Это не похоже на работу. Я предполагаю, что он считает string_to_replaceстроковый литерал, а не переменную, представляющую строку. Можно ли создавать регулярные выражения JavaScript на лету, используя строковые переменные? Примерно так было бы замечательно, если вообще возможно:

function replace_foo(target, string_to_replace, replacement) {
   var regex = "/^" + string_to_replace + "/";
   return target.replace(regex,replacement);
}
buley
источник

Ответы:

215

Там же , new RegExp(string, flags)где flagsесть gили i. Так

'GODzilla'.replace( new RegExp('god', 'i'), '' )

оценивает

zilla
медер омуралиев
источник
31
И опустите /разделители регулярных выражений при использовании этой формы тоже.
cdhowie
111

Со строковыми литералами это достаточно просто.

На самом деле, нет! Пример заменяет только первое вхождение string_to_replace. Чаще всего вы хотите заменить все вхождения, в этом случае вам нужно преобразовать строку в глобальный ( /.../g) RegExp. Вы можете сделать это из строки, используя new RegExpконструктор:

new RegExp(string_to_replace, 'g')

Проблема в том, что любые специальные символы регулярных выражений в строковом литерале будут вести себя по-своему, а не как обычные символы. Вы должны были бы их убрать, чтобы это исправить. К сожалению, для этого нет встроенной функции, поэтому вы можете использовать ее:

function escapeRegExp(s) {
    return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}

Также обратите внимание, что когда вы используете RegExp replace(), строка замены теперь также имеет специальный символ $. Этого также необходимо избежать, если вы хотите, чтобы $в вашем тексте замены был литерал !

function escapeSubstitute(s) {
    return s.replace(/\$/g, '$$$$');
}

(Четыре $с, потому что это сама строка замены - аааа!)

Теперь вы можете реализовать глобальную замену строк с помощью RegExp:

function replace_foo(target, string_to_replace, replacement) {
    var relit= escapeRegExp(string_to_replace);
    var sub= escapeSubstitute(replacement);
    var re= new RegExp(relit, 'g');
    return target.replace(re, sub);
}

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

s.split(string_to_replace).join(replacement)

...и это все. Это общепринятая идиома.

говорят, что я хочу заменить все, кроме string_to_replace

Что это значит, вы хотите заменить все фрагменты текста, не участвуя в сопоставлении со строкой? Замена на ^определенно не делает этого, потому что ^означает токен начала строки, а не отрицание. ^это только отрицание в []группах персонажей. Есть и негативные перспективы (?!...), но в JScript есть проблемы с этим, поэтому вам следует избегать этого.

Вы можете попробовать сопоставить строку «все до» и использовать функцию, чтобы отбросить любое пустое натяжение между совпадающими строками:

var re= new RegExp('(.*)($|'+escapeRegExp(string_to_find)+')')
return target.replace(re, function(match) {
    return match[1]===''? match[2] : replacement+match[2];
});

Здесь, опять же, разделение может быть проще:

var parts= target.split(string_to_match);
for (var i= parts.length; i-->0;)
    if (parts[i]!=='')
        parts[i]= replacement;
return parts.join(string_to_match);
bobince
источник
10

Как уже говорили другие, используйте new RegExp(pattern, flags)для этого. Стоит отметить, что вы будете передавать строковые литералы в этот конструктор, поэтому каждый обратный слеш должен быть экранирован. Например, если вы хотите, чтобы ваше регулярное выражение совпадало с обратной косой чертой, вам нужно было бы сказать new RegExp('\\\\'), тогда как литерал регулярного выражения должен был бы быть только таким /\\/. В зависимости от того, как вы собираетесь использовать это, вам следует с осторожностью передавать пользовательский ввод в такую ​​функцию без адекватной предварительной обработки (экранирование специальных символов и т. Д.). Без этого ваши пользователи могут получить очень неожиданные результаты.

Кент
источник
3
Этот ответ, хотя и не самый подробный, в нем упоминается важная деталь, на которой я только что застрял в течение часа: избегайте любых специальных последовательностей. Например, я искал слово, начинающееся с определенного термина, поэтому мне нужно регулярное выражение /\b[term]\B/, но при его создании мне нужно вызвать new RegExp("\\b"+ term + "\\B"). Небольшой , но важное различие, и трудно определить , так как использовать его в качестве регулярного выражения непосредственно делает работу , как и ожидалось.
Бисон
0

Я думаю, у меня есть очень хороший пример для выделения текста в строке (он находит не глядя на регистр, но выделенный с помощью регистра)

function getHighlightedText(basicString, filterString) {

    if ((basicString === "") || (basicString === null) || (filterString === "") || (filterString === null)) return basicString;

    return basicString.replace(new RegExp(filterString.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\\\$&'), 'gi'),
        function(match)
            {return "<mark>"+match+"</mark>"});

}

http://jsfiddle.net/cdbzL/1258/

Журов Константин
источник
0

Это действительно простое решение:

function replace(target, string_to_replace, replacement) {
  return target.split(string_to_replace).join(replacement);
}

Нет необходимости в регулярных выражениях вообще

Это также, кажется, самый быстрый в современных браузерах https://jsperf.com/replace-vs-split-join-vs-replaceall

Джек Аллан
источник