Buat RegExps dengan cepat menggunakan variabel string

139

Katakanlah saya ingin membuat yang berikut ini dapat digunakan kembali:

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

Saya mungkin melakukan sesuatu seperti ini:

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

Dengan string literal ini cukup mudah. Tetapi bagaimana jika saya ingin sedikit lebih rumit dengan regex? Misalnya, saya ingin mengganti semuanya tetapi string_to_replace . Secara naluriah saya akan mencoba memperluas hal di atas dengan melakukan sesuatu seperti:

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

Ini sepertinya tidak berhasil. Dugaan saya adalah yang dianggapnya string_to_replaceadalah string literal, bukan variabel yang mewakili string. Apakah mungkin membuat ekspresi reguler JavaScript dengan cepat menggunakan variabel string? Sesuatu seperti ini akan sangat bagus jika memungkinkan:

function replace_foo(target, string_to_replace, replacement) {
   var regex = "/^" + string_to_replace + "/";
   return target.replace(regex,replacement);
}
buley
sumber

Jawaban:

216

Ada new RegExp(string, flags)di mana flagsyang gatau i. Begitu

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

mengevaluasi ke

zilla
meder omuraliev
sumber
32
Dan hilangkan /pemisah ekspresi reguler saat menggunakan formulir ini juga.
cdhowie
111

Dengan literal string, ini cukup mudah.

Tidak juga! Contoh tersebut hanya menggantikan kemunculan pertamastring_to_replace . Lebih umum Anda ingin mengganti semua kejadian, dalam hal ini, Anda harus mengubah string menjadi /.../gRegExp global ( ). Anda dapat melakukan ini dari string menggunakan new RegExpkonstruktor:

new RegExp(string_to_replace, 'g')

Masalah dengan ini adalah bahwa karakter khusus-regex dalam string literal akan berperilaku dengan cara khusus mereka alih-alih menjadi karakter normal. Anda harus melakukan backslash-escape untuk memperbaikinya. Sayangnya, tidak ada fungsi bawaan untuk melakukan ini untuk Anda, jadi inilah yang dapat Anda gunakan:

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

Perhatikan juga bahwa saat Anda menggunakan RegExp di replace(), string pengganti sekarang memiliki karakter khusus juga $,. Ini juga harus di-escape jika Anda ingin memiliki literal $dalam teks pengganti Anda!

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

(Empat $d karena itu sendiri adalah string pengganti — argh!)

Sekarang Anda dapat mengimplementasikan penggantian string global dengan 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);
}

Sakit sekali. Untungnya jika yang ingin Anda lakukan hanyalah mengganti string lurus tanpa bagian regex tambahan, ada cara yang lebih cepat:

s.split(string_to_replace).join(replacement)

... dan itu saja. Ini adalah idiom yang umum dipahami.

katakanlah saya ingin mengganti semuanya kecuali string_to_replace

Apa artinya, Anda ingin mengganti semua hamparan teks yang tidak mengambil bagian dalam pertandingan melawan string? Pengganti dengan ^pasti bukan ini, karena ^berarti token awal string, bukan negasi. ^hanya negasi dalam []kelompok karakter. Ada juga lookaheads negatif (?!...), tetapi ada masalah dengan itu di JScript jadi sebaiknya Anda menghindarinya.

Anda dapat mencoba mencocokkan 'semuanya hingga' string, dan menggunakan fungsi untuk membuang peregangan kosong di antara string yang cocok:

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

Di sini, sekali lagi, pemisahan mungkin lebih sederhana:

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
sumber
11

Seperti yang dikatakan orang lain, gunakan new RegExp(pattern, flags)untuk melakukan ini. Perlu diperhatikan bahwa Anda akan meneruskan string literal ke konstruktor ini, jadi setiap garis miring terbalik harus di-escape. Jika, misalnya Anda ingin ekspresi reguler Anda cocok dengan garis miring terbalik, Anda harus mengatakannya new RegExp('\\\\'), sedangkan literal ekspresi reguler hanya perlu /\\/. Bergantung pada bagaimana Anda bermaksud menggunakan ini, Anda harus berhati-hati dalam meneruskan input pengguna ke fungsi seperti itu tanpa pra-pemrosesan yang memadai (meng-escape karakter khusus, dll.) Tanpa ini, pengguna Anda mungkin mendapatkan beberapa hasil yang sangat tidak terduga.

Kent
sumber
4
Jawaban ini, meski bukan yang paling mendetail, memang menyebutkan detail krusial yang baru saja saya pertahankan selama satu jam: lolos dari urutan khusus. Misalnya, saya sedang mencari kata yang dimulai dengan istilah tertentu, jadi regex yang saya perlukan adalah /\b[term]\B/, tetapi saat membuatnya, saya perlu memanggil new RegExp("\\b"+ term + "\\B"). Kecil tapi perbedaan penting, dan sulit untuk spot karena menggunakannya sebagai regex langsung melakukan pekerjaan seperti yang diharapkan.
Byson
0

Saya pikir saya memiliki contoh yang sangat bagus untuk menyorot teks dalam string (tidak melihat register tetapi disorot menggunakan register)

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/

Zhurov Konstantin
sumber
0

Solusi yang sangat sederhana untuk ini adalah:

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

Tidak perlu Regex sama sekali

Ini juga tampaknya menjadi yang tercepat di browser modern https://jsperf.com/replace-vs-split-join-vs-replaceall

Jack Allan
sumber