Javascript'te bir RegExp.escape işlevi var mı?


442

Sadece olası bir dize dışında düzenli bir ifade oluşturmak istiyorum.

var usersString = "Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches = "Hello".match(expression);

Bunun için yerleşik bir yöntem var mı? Değilse, insanlar ne kullanır? Ruby var RegExp.escape. Kendi yazmam gerekmiyormuş gibi hissetmiyorum, orada standart bir şey olmalı. Teşekkürler!


15
Sadece RegExp.escapeüzerinde çalışılan ince halk güncellemek istedim ve değerli girdisi olduğunu düşünen herkes katkıda bulunmak çok hoş geldiniz. core-js ve diğer çoklu dolgular bunu sunuyor.
Benjamin Gruenbaum

Yanıtlar:


573

Yukarıda bağlı olan işlev yetersiz. Bir karakter grubunda aralıklar için kullanılan ( veya dizenin başlangıcı ve bitişi) ^veya kaçamaz .$-

Bu işlevi kullanın:

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

İlk bakışta gereksiz gibi görünse de, kaçmak -(yanı sıra ^) işlevi, bir karakter sınıfına ve normal ifadenin gövdesine eklenecek karakterlerden kaçmak için uygun hale getirir.

Kaçış /işlevi, sonraki değerlendirme için bir JS normal ifadesinde kullanılacak karakterlerden kaçmak için işlevi uygun hale getirir.

Her ikisinden de kaçmanın bir dezavantajı olmadığından, daha geniş kullanım durumlarını kapsamak için kaçmak mantıklıdır.

Ve evet, bunun standart JavaScript'in bir parçası olmaması hayal kırıklığı yaratıyor.


16
Aslında, biz kaçmaya gerek yok /hiç
Thorn

28
@Paul: Perl quotemeta( \Q), Python re.escape, PHP preg_quote, Ruby Regexp.quote...
bobince

13
Bu işlevi bir döngüde kullanacaksanız, RegExp nesnesini kendi değişkeni yapmak en iyisidir var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;ve sonra işleviniz return s.replace(e, '\\$&');Bu şekilde RegExp'i yalnızca bir kez somutlaştırırsınız.
17:13, styfle

15
Yerleşik nesneleri çoğaltmaya karşı standart argümanlar burada geçerlidir, değil mi? ECMAScript'in gelecekteki bir sürümü, RegExp.escapesizinkinden farklı bir uygulama sağlıyorsa ne olur ? Bu işlevin hiçbir şeye bağlı olmaması daha iyi olmaz mıydı?
Mark Amery

15
eslint görüşüne için bobince umurunda değil
bobince

114

Lodash kullanan herkes için, v3.0.0'dan beri bir _.escapeRegExp işlevi yerleşiktir:

_.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)'

Ve tam lodash kütüphanesine ihtiyaç duymamanız durumunda, sadece bu fonksiyona ihtiyacınız olabilir !


6
sadece bunun bir npm paketi bile var! npmjs.com/package/lodash.escaperegexp
Ted Pennings

1
Bu, böyle basit bir şey için gerçekten orada olması gerekmeyen bir sürü kod alır. Bobince'nin cevabını kullan ... benim için ve yükleyecek çok daha az bayt için çalışıyor!
Rob Evans

6
@RobEvans ile cevabım başlar "herkes için lodash kullanarak" , ve hatta sen gerektirebilir söz sadeceescapeRegExp işlevi.
gustavohenke

2
@gustavohenke Üzgünüm biraz daha net olmalıydım, bağlı olduğunuz modülü "sadece bu işleve" dahil ettim ve yorum yaptığım da buydu. Bir göz atarsanız, içinde tek bir regexp ile etkili bir şekilde tek bir işlev olması gereken şey için oldukça fazla kod var. Halihazırda lodash kullanıyorsanız, onu kullanmak mantıklıdır, ancak diğer yanıtı kullanın. Belirsiz yorum için özür dilerim.
Rob Evans

2
@maddob Bunu göremiyorum \ x3 bahsettiğiniz: Kaçan
tellerim

43

Buradaki ifadelerin çoğu tek kullanımlık durumları çözer.

Sorun değil, ama "her zaman işe yarar" yaklaşımını tercih ederim.

function regExpEscape(literal_string) {
    return literal_string.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&');
}

Bu, normal ifadelerde aşağıdaki kullanımlardan herhangi biri için değişmez bir dizeden "tamamen kaçar":

  • Normal ifadeye ekleme. Örneğinnew RegExp(regExpEscape(str))
  • Bir karakter sınıfına ekleme. Örneğinnew RegExp('[' + regExpEscape(str) + ']')
  • Tamsayı sayısı belirleyicisine ekleme. Örneğinnew RegExp('x{1,' + regExpEscape(str) + '}')
  • JavaScript olmayan normal ifade motorlarında yürütme.

Kapsanan Özel Karakterler:

  • -: Karakter sınıfında bir karakter aralığı oluşturur.
  • [/ ]: Karakter sınıfını başlatır / bitirir.
  • {/ }: Bir numara belirleyiciyi başlatır / sonlandırır.
  • (/ ): Bir grubu başlatır / bitirir.
  • */ +/ ?: Tekrarlama türünü belirtir.
  • .: Herhangi bir karakterle eşleşir.
  • \: Karakterlerden kaçar ve objeleri başlatır.
  • ^: Eşleme bölgesinin başlangıcını belirtir ve bir karakter sınıfındaki eşleşmeyi reddeder.
  • $: Eşleşen bölgenin sonunu belirtir.
  • |: Değişimi belirtir.
  • #: Boş aralık modunda yorumu belirtir.
  • \s: Boş aralık modunda yok sayılır.
  • ,: Sayı belirleyicisindeki değerleri ayırır.
  • /: İfadeyi başlatır veya bitirir.
  • :: Özel grup türlerini ve Perl tarzı karakter sınıflarının bir bölümünü tamamlar.
  • !: Sıfır genişlikli grubu olumsuzlar.
  • </ =: Sıfır genişlikli grup özelliklerinin bir parçası.

Notlar:

  • /düzenli ifadenin herhangi bir çeşidinde kesinlikle gerekli değildir. Bununla birlikte, birisinin (ürperti) yapması durumunda korur eval("/" + pattern + "/");.
  • , dizenin sayısal belirleyicide bir tamsayı olması gerekiyorsa, sessizce yanlış derlemek yerine bir RegExp derleme hatasına neden olur.
  • #ve \sJavaScript'ten kaçmasına gerek yoktur, ancak diğer birçok aromada da kullanılır. Düzenli ifadenin daha sonra başka bir programa geçmesi durumunda buradan kaçarlar.

Ayrıca, düzenli ifadeyi JavaScript normal ifade motoru özelliklerine olası eklemelere karşı gelecekte kanıtlamanız gerekiyorsa, daha paranoyak kullanmanızı öneririz:

function regExpEscapeFuture(literal_string) {
    return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}

Bu işlev, gelecekteki düzenli ifade tatlarında sözdizimi için kullanılmayacağı açıkça garanti edilenler dışında her karakterden kaçar.


Gerçekten sanitasyon meraklısı için, bu son durumu düşünün:

var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')');

Bu gerektiğini değil diğer bazı tatlar JavaScript cezası derlemek, ama olacak. Başka bir lezzete geçmek niyetinde ise, null durumu s === ''bağımsız olarak kontrol edilmelidir, şöyle:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');

1
/Kaçan edilmesi gerekmez [...]karakter sınıfı.
Dan Dascalescu

1
Bunların çoğunun kaçmasına gerek yok. "Karakter sınıfında bir karakter aralığı oluşturur" - asla dizenin içinde bir karakter sınıfında bulunmazsınız. "Boş aralık modunda yorumu belirtir, Boş aralık modunda yoksayılır" - javascript'te desteklenmez. " Sayı belirleyicisindeki değerleri ayırır " - hiçbir zaman dizenin içindeki sayı belirleyicisinde olmazsınız. Ayrıca, kamera özellikleri içinde rastgele metin yazamazsınız. "İfadeyi başlatır veya bitirir" - kaçmaya gerek yoktur. Eval çok daha fazla kaçmayı gerektireceği için bir durum değildir. [bir sonraki yorumda devam edecek]
Qwertiy

"Özel grup türlerini ve Perl tarzı karakter sınıflarının bir bölümünü tamamlar" - javascript'te mevcut değil gibi görünüyor. "Sıfır genişlikli grubu olumsuzlar, Sıfır genişlikli grup özelliklerinin bir parçasıdır" - dizenin içinde hiçbir zaman gruplarınız olmaz.
Qwertiy

@Qwertiy Bu ekstra kaçışların nedeni, belirli kullanım durumlarında sorunlara yol açabilecek kenar durumlarını ortadan kaldırmaktır. Örneğin, bu işlevin kullanıcısı, kaçan normal ifade dizesini bir grubun parçası olarak başka bir normal ifadeye, hatta Javascript dışında başka bir dilde kullanmak isteyebilir. Fonksiyon "Asla bir karakter sınıfının parçası olmayacağım" gibi varsayımlar yapmaz, çünkü genel olması gerekir . Daha YAGNI yaklaşımı için buradaki diğer yanıtlardan herhangi birine bakın.
Pi Marillion

Çok iyi. _ Olsa da neden kaçmıyor? Muhtemelen daha sonra normal ifade sözdizimi haline gelmemesini sağlayan nedir?
madprops


21

JQueryUI'nin otomatik tamamlama widget'ında (sürüm 1.9.1) biraz farklı bir normal ifade kullanırlar (Satır 6753), @bobince yaklaşımıyla birleştirilmiş normal ifade.

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}

4
Tek fark, kaçmaları ,(bir metakarakter değil) ve #ve yalnızca boş aralık modunda önemli olan (JavaScript tarafından desteklenmeyen) boşluk olmasıdır. Ancak, eğik çizgiden kaçmamak doğru olur.
Martin Ender

18
Kodu yerel olarak yapıştırmak yerine jquery UI'nin uygulamasını yeniden kullanmak istiyorsanız, ile devam edin $.ui.autocomplete.escapeRegex(myString).
Scott Stafford

2
lodash da buna sahip, _. escapeRegExp ve npmjs.com/package/lodash.escaperegexp
Ted Pennings

v1.12 aynı, tamam!
Peter Krauss

13

Hiçbir şey sadece alfasayısal olmayan her karakterden kaçmanızı engellemez:

usersString.replace(/(?=\W)/g, '\\');

Yaparken belli derecede okunabilirliği kaybedersiniz, re.toString()ancak çok fazla basitlik (ve güvenlik) kazanırsınız.

ECMA-262 göre, bir yandan düzenli ifade "sözdizimi karakterler", alfanümerik olmayan daima sonuç güvenli ve özel kaçış dizileri (şekildedir \d, \w, \n) her zaman alfanümerik olan bu tür hiçbir yanlış kumanda kaçar üretilen olacağını .


Basit ve etkili. Bunu kabul edilen cevaptan çok daha iyi seviyorum. (Gerçekten) eski tarayıcılar .replace(/[^\w]/g, '\\$&')için aynı şekilde çalışır.
Tomas Langkaas

6
Bu Unicode modunda başarısız olur. Örneğin, bir vekil çiftin her bir kod birimini ayrı ayrı eşleştirerek geçersiz çıkış kodlarına neden new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')olacağından istisna atar \W.
Alexey Lebedev

1
alternatif:.replace(/\W/g, "\\$&");
Miguel Pynto 21:18

@AlexeyLebedev Hes cevap Unicode modunu işlemek için düzeltildi mi? Yoksa başka bir yerde bu basitliği korurken bir çözüm var mı?
johny neden


6

Bu daha kısa bir versiyon.

RegExp.escape = function(s) {
    return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
}

Bu olmayan meta karakterler içerir %, &, ', ve ,, ancak JavaScript RegExp şartname buna izin verir.


2
Karakter aralıkları karakter listesini gizlediğinden, bu "daha kısa" sürümü kullanmazdım, bu da ilk bakışta doğruluğu doğrulamayı zorlaştırır.
nhahtdh

@nhahtdh Muhtemelen ben de istemezdim, ama bilgi için buraya gönderildi.
KZH

@kzh: "bilgi için" yayınlamak, anlayış için yayınlamaktan daha az yardımcı olur. Cevabımın daha net olduğu konusunda hemfikir olmaz mısın ?
Dan Dascalescu

En azından .kaçırıldı. Ve (). Ya da değil? [-^garip. Orada ne olduğunu hatırlamıyorum.
Qwertiy

Bunlar belirtilen aralıktadır.
kzh


3

Sadece normal ifadenizde sorunlara neden olacak karakterlerden kaçmak yerine (ör. Bir kara liste), bunun yerine neden beyaz liste kullanmayı düşünmüyorsunuz? Bu şekilde, her karakter eşleşmediği takdirde renkli olarak kabul edilir.

Bu örnek için aşağıdaki ifadeyi varsayalım:

RegExp.escape('be || ! be');

Bu, harfleri, sayıları ve boşlukları beyaz listeye ekler:

RegExp.escape = function (string) {
    return string.replace(/([^\w\d\s])/gi, '\\$1');
}

İadeler:

"be \|\| \! be"

Bu, kaçması gerekmeyen karakterlerden kaçabilir, ancak bu ifadenizi engellemez (belki bazı küçük zaman cezaları - ancak güvenlik için buna değer).


Bu, @ filip'in cevabından farklı mı? stackoverflow.com/a/40562456/209942
johny neden

3
escapeRegExp = function(str) {
  if (str == null) return '';
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

1

Diğer cevaplardaki işlevler, tüm normal ifadelerden kaçmak için aşırı derecede doludur ( daha sonra daha büyük regexps olarak birleştirilecek normal ifadelerin bölümlerinden kaçmak için yararlı olabilir ).

Eğer bütün bir regexp kaçmak ve ya tek başına olan meta karakterler alıntı, onunla yapılır (Eğer ., ?, +, *, ^, $, |, \şey) ya da başlatmak ( (, [, {) tek ihtiyacınız olan:

String.prototype.regexEscape = function regexEscape() {
  return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
};

Ve evet, JavaScript'in bu gibi bir işleve sahip olmaması hayal kırıklığı yaratıyor.


Diyelim ki kullanıcı girişinden kaçtınız (text)nextve içeri girdiniz: (?:+ input + ). Yönteminiz, (?:\(text)next)derlenemeyen sonuçta elde edilen dizeyi verecektir . Bu oldukça makul bir ekleme olduğunu unutmayın, re\+ input + gibi bazı deli değil re(bu durumda, programcı aptalca bir şey yapmakla
suçlanabilir

1
@nhahtdh: Cevabım, normal ifadelerin tümünden kaçtığını ve regexps'in parçalarını (veya gelecekteki parçalarını değil) onlarla "yapıldığını" özellikle belirtti. Nazikçe geri al?
Dan Dascalescu

Tüm ifadeden kaçmanız nadiren olur - değişmez dize ile çalışmak istiyorsanız normal ifadeye kıyasla çok daha hızlı olan dize işlemi vardır.
nhahtdh

Bu, yanlış olduğundan bahsetmiyor - \regex'iniz \wbozulmadan kalacağı için kaçmalı . Ayrıca, JavaScript izlemeye izin vermiyor gibi görünüyor ), en azından Firefox bunun için hata veriyor.
nhahtdh

1
Lütfen kapanış ile ilgili bölüme hitap edin)
nhahtdh

1

Başka bir (çok daha güvenli) yaklaşım, unicode kaçış biçimini kullanarak tüm karakterlerden (ve şu anda bildiğimiz birkaç özel karakterden değil) kaçmaktır \u{code}:

function escapeRegExp(text) {
    return Array.from(text)
           .map(char => `\\u{${char.charCodeAt(0).toString(16)}}`)
           .join('');
}

console.log(escapeRegExp('a.b')); // '\u{61}\u{2e}\u{62}'

uBu yöntemin çalışması için bayrağı geçmeniz gerektiğini lütfen unutmayın :

var expression = new RegExp(escapeRegExp(usersString), 'u');

1


Değişmez sayılabilmesi için kaçması gereken 12 meta karakter olmuştur .

Dengeli bir
regex sargıya yerleştirilen kaçan dize ile ne yapıldığı önemli değil, önemli değil.

Bunu kullanarak bir dize değiştirme yapın

var escaped_string = oldstring.replace( /[\\^$.|?*+()[{]/g, '\\$&' );

Ne hakkında ]?
Thomasleveil
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.