Створюйте 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);
}

Відповіді:


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, '$$$$');
}

(Чотири $s, тому що це сама по собі заміна рядка - 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)

... і це все. Це загальнозрозуміла ідіома.

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

10

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


3
Ця відповідь, хоч і не є найбільш детальною, але згадує важливу деталь, яку я просто затримав протягом години: уникайте будь-яких спеціальних послідовностей. Наприклад, я шукав слово, починаючи з певного терміна, тому мені потрібен регулярний вираз /\b[term]\B/, але при його побудові мені потрібно зателефонувати new RegExp("\\b"+ term + "\\B"). Невеликий , але важлива відмінність, і важко визначити , так як використовувати його в якості регулярного виразу безпосередньо робить роботу , як і очікувалося.
Byson


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/


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.