Можете ли вы создавать регулярные выражения JavaScript "на лету", используя строковые переменные?

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

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);
}

Ответ 1

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

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

оценивается как

zilla

Ответ 2

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

Не совсем! Этот пример заменяет первое появление 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, '$$$$');
}

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

Теперь вы можете реализовать глобальную замену строк с помощью 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)

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

say Я хочу заменить все, кроме 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);

Ответ 3

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

Ответ 5

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

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/