Global bayraklı bir RegExp neden yanlış sonuçlar veriyor?


277

Genel bayrağı ve büyük / küçük harf duyarsız bayrağı kullandığımda bu normal ifade ile ilgili sorun nedir? Sorgu, kullanıcı tarafından oluşturulan bir girdidir. Sonuç [doğru, doğru] olmalıdır.

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]

var reg = /^a$/g;
for(i = 0; i++ < 10;)
   console.log(reg.test("a"));


54
JavaScript'te RegExp'in birçok tuzağından birine hoş geldiniz. Tuhaf yan etkiler ve belirsiz uyarılarla dolu, şimdiye kadar tanıştığım regex işlemenin en kötü arayüzlerinden birine sahip. Normal regex ile yapmak istediğiniz ortak görevlerin çoğunun doğru yazılması zordur.
bobince

XRegExp iyi bir alternatif gibi görünüyor. xregexp.com
yaklaşık

Cevabı burada da görebilirsiniz: stackoverflow.com/questions/604860/…
Prestaul

Bir çözüm, eğer onunla kurtulabiliyorsanız, regex değişmezini kaydetmek yerine doğrudan kullanmaktır re.
thdoan

Yanıtlar:


350

RegExpNesne izler lastIndexbir maç oluştuğu bu nedenle daha sonraki maçları da bir göz atın 0 yerine, son kullanılan dizinden başlayacak:

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));

alert(re.lastIndex);

result.push(re.test('Foo Bar'));

lastIndexHer testten sonra manuel olarak 0'a sıfırlamak istemiyorsanız , gbayrağı kaldırın .

İşte özelliklerin dikte ettiği algoritma (bölüm 15.10.6.2):

RegExp.prototype.exec (dize)

Dizenin normal ifadeye karşı normal ifade eşleşmesini gerçekleştirir ve eşleşmenin sonuçlarını içeren bir Array nesnesi döndürür veya dize eşleşmezse null değeri ToString (dize) dizesi, normal ifade deseninin bir oluşumu için aşağıdaki gibi aranır:

  1. S ToString (string) değeri olsun.
  2. Uzunluk S'nin uzunluğu olsun.
  3. LastIndex öğesinin lastIndex özelliğinin değeri olmasına izin verin.
  4. Ben ToInteger (lastIndex) değeri olsun.
  5. Global özelliği false olursa, i = 0 olsun.
  6. I <0 veya I> uzunluğu varsa, lastIndex öğesini 0 olarak ayarlayın ve null değerini döndürün.
  7. S ve i değişkenlerini vererek [[Match]] 'i çağırın. [[Maç]] hata döndürdüyse, 8. adıma gidin; aksi takdirde r'nin Devlet sonucu olmasına izin verin ve 10. adıma gidin.
  8. İ = i + 1 olsun.
  9. 6. adıma gidin.
  10. E'nin r'nin endIndex değeri olmasına izin verin.
  11. Global özellik true ise, lastIndex öğesini e olarak ayarlayın.
  12. N, r'nin yakalama dizisinin uzunluğu olsun. (Bu, 15.10.2.1'in NCapturingParens'iyle aynı değerdir.)
  13. Aşağıdaki özelliklere sahip yeni bir dizi döndürün:
    • İndex özelliği, S dizesinin tamamında eşleşen alt dizenin konumuna ayarlanır.
    • İnput özelliği S olarak ayarlanmıştır.
    • Length özelliği n + 1 olarak ayarlanmıştır.
    • 0 özelliği eşleşen alt dizeye ayarlanır (yani S'nin ofset i dahil ve ofset e hariç arasındaki kısmı).
    • I> 0 ve I ≤ n olacak şekilde her tam sayı i için, ToString (i) adlı özelliği r'nin yakalar dizisinin ith öğesine ayarlayın.

83
Bu, otostopçunun Galaxy API tasarım kılavuzu. "Eğer kontrol etmek için rahatsız olsaydı, düştü Bu tuzak birkaç yıl boyunca spec mükemmel belgelenmiştir"
Retsam

5
Firefox'un yapışkan bayrağı, ne demek istediğinizi yapmaz. Aksine, normal ^ ifadesinin başında bir ^ varmış gibi davranır, ancak bu ^ dizenin başlangıcı yerine geçerli dize konumuyla (lastIndex) eşleşir . Normal ifadenin "lastIndex'ten sonraki herhangi bir yer" yerine "tam burada" ile eşleşip eşleşmediğini etkili bir şekilde test edersiniz. Sağladığınız bağlantıya bakın!
Doin

1
Bu cevabın açılış cümlesi doğru değil. Hiçbir şey söylemeyen spesifikasyonun 3. adımını vurguladınız. Gerçek etkisi lastIndex5., 6. ve 11. adımlardadır. Açılış ifadeniz yalnızca KÜRESEL BAYRAK AYARLANMIŞTIR.
Prestaul

@Prestaul evet, küresel bayraktan bahsetmediğinden haklısın. Sorunun çerçevelenmesi nedeniyle muhtemelen (o zaman ne düşündüğümü hatırlayamıyorum) örtüktü. Cevabı düzenlemekten veya silmekten çekinmeyin ve cevabınıza bağlantı verin. Ayrıca, benden daha iyi olduğun konusunda sana güvence vermeme izin ver. Zevk almak!
Ionuț G. Stan

@ IonuțG.Stan, önceki yorumum saldırgan görünüyorsa özür dilerim, niyetim bu değildi. Bu noktada düzenleyemiyorum, ama sadece yorumumun önemli noktasına dikkat çekmek için bağırmaya çalışmıyordum. Benim hatam!
Ağustos'ta Prestaul

72

Tek bir RegExpnesne kullanıyorsunuz ve birden çok kez çalıştırıyorsunuz. Her ardışık yürütmede son maç endeksinden devam eder.

Her yürütmeden önce baştan başlamak için normal ifadeyi "sıfırlamanız" gerekir:

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]

Her seferinde yeni bir RegExp nesnesi oluşturmanın daha okunabilir olabileceğini söyledikten sonra (RegExp zaten önbelleğe alındığı için ek yük minimumdur):

result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));

1
Ya da sadece gbayrağı kullanmayın .
melpomene

36

RegExp.prototype.testlastIndexher ifadenin son testin durduğu yerden başlaması için normal ifadelerin özelliğini günceller . Özelliği String.prototype.matchgüncellemediğinden kullanmanızı öneririm lastIndex:

!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true

Not: !!bunu bir boole'ye dönüştürür ve ardından sonucu yansıtacak şekilde boolean'ı tersine çevirir.

Alternatif olarak, lastIndexözelliği sıfırlayabilirsiniz :

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));

11

Global gbayrağın kaldırılması sorununuzu çözecektir.

var re = new RegExp(query, 'gi');

Olmalı

var re = new RegExp(query, 'i');

0

Re.lastIndex = 0 ayarlamanız gerekir, çünkü g flag regex ile son eşleşmeyi takip edin, bu yüzden test aynı dizeyi test etmeye gitmeyecektir, çünkü re.lastIndex = 0

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
re.lastIndex=0;
result.push(re.test('Foo Bar'));

console.log(result)



-1

Ben işlevi vardı:

function parseDevName(name) {
  var re = /^([^-]+)-([^-]+)-([^-]+)$/g;
  var match = re.exec(name);
  return match.slice(1,4);
}

var rv = parseDevName("BR-H-01");
rv = parseDevName("BR-H-01");

İlk çağrı çalışır. İkinci çağrı yapmaz. sliceOperasyon boş değeri hakkında şikayet ediyor. Bunun sebebi olduğunu düşünüyorum re.lastIndex. Bu garip çünkü yeni bir şey beklerdimRegExp işlev her çağrıldığında tahsis edilmesini beklerim ve işlevimin birden çok çağrısında paylaşılmaz.

Bunu şu şekilde değiştirdiğimde:

var re = new RegExp('^([^-]+)-([^-]+)-([^-]+)$', 'g');

O zaman lastIndexbekletme etkisi elde edemiyorum . Beklediğim gibi çalışıyor.

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.