it-swarm.com.de

Warum führt eine RegExp mit globaler Markierung zu falschen Ergebnissen?

Was ist das Problem mit diesem regulären Ausdruck, wenn ich das globale Flag und das Kennzeichen für Groß- und Kleinschreibung verwende? Abfrage ist eine vom Benutzer generierte Eingabe. Das Ergebnis sollte [true, true] sein.

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

227
about

Das RegExp-Objekt verfolgt die lastIndex , bei der eine Übereinstimmung aufgetreten ist. Bei nachfolgenden Übereinstimmungen beginnt es mit dem zuletzt verwendeten Index anstelle von 0. Schauen Sie sich das an:

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

Wenn Sie lastIndex nicht nach jedem Test manuell auf 0 zurücksetzen möchten, entfernen Sie einfach das Flag g.

Hier ist der Algorithmus, den die Spezifikationen vorgeben (Abschnitt 15.10.6.2):

RegExp.prototype.exec (Zeichenfolge)

Führt ein regulärer Ausdruck der Zeichenfolge gegen den regulären Ausdruck und gibt ein Array-Objekt zurück, das die .__ enthält. Ergebnisse der Übereinstimmung oder Null, wenn die Zeichenfolge stimmte nicht mit der Zeichenfolge ToString (string) wird nach einem .__ durchsucht. Auftreten des regulären Ausdrucks Muster wie folgt:

  1. Sei S der Wert von ToString (string).
  2. Länge sei die Länge von S.
  3. Let lastIndex ist der Wert der lastIndex-Eigenschaft.
  4. Sei ich der Wert von ToInteger (lastIndex).
  5. Wenn die globale Eigenschaft falsch ist, gilt i = 0.
  6. Wenn I <0 oder I> length, dann lastIndex auf 0 setzen und NULL zurückgeben.
  7. Rufen Sie [[Match]] auf, und geben Sie die Argumente S und i an. Wenn [[Übereinstimmung]] Fehler zurückgegeben, weiter mit Schritt 8; andernfalls sei r sein Zustandsergebnis und fahren Sie mit Schritt 10 fort.
  8. Sei i = i + 1.
  9. Weiter mit Schritt 6.
  10. Sei e der EndIndex-Wert von r.
  11. Wenn die globale Eigenschaft true ist, setzen Sie lastIndex auf e.
  12. Sei n die Länge von rs Captures-Array. (Dies ist der gleiche Wert wie bei 15.10.2.1 NCapturingParens.)
  13. Gibt ein neues Array mit den folgenden Eigenschaften zurück:
    • Der Index Eigenschaft wird auf die Position von .__ gesetzt. übereinstimmende Teilzeichenfolge innerhalb des vollständigen Zeichenfolge S.
    • Die Eingabeeigenschaft ist festgelegt zu S.
    • Die length-Eigenschaft ist auf .__ gesetzt. n + 1.
    • Die 0-Eigenschaft wird auf die .__ gesetzt. übereinstimmende Teilzeichenfolge (d. h. der Teil von S zwischen Offset i inklusive und Offset e exklusive).
    • Für jeden ganze Zahl i, so dass I> 0 und I ≤ n, Setzen Sie die Eigenschaft mit dem Namen ToString (i) auf Das i-te Element von r erfasst das Array.
289
Ionuț G. Stan

Sie verwenden ein einzelnes RegExp-Objekt und führen es mehrfach aus. Bei jeder nachfolgenden Ausführung wird der letzte Übereinstimmungsindex fortgesetzt.

Sie müssen den Regex "zurücksetzen", um vor jeder Ausführung am Anfang zu beginnen:

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

Allerdings ist es möglicherweise lesbarer, jedes Mal ein neues RegExp-Objekt zu erstellen (der Overhead ist minimal, da RegExp trotzdem im Cache gespeichert wird):

result.Push((/Foo B/gi).test(stringA));
result.Push((/Foo B/gi).test(stringB));
63
Roatin Marth

RegExp.prototype.testaktualisiert die regulären Ausdrücke 'lastIndex, damit jeder Test dort beginnt, wo der letzte gestoppt wurde. Ich würde vorschlagen,String.prototype.matchzu verwenden, da dielastIndex-Eigenschaft nicht aktualisiert wird:

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

Hinweis:!!konvertiert es in einen Boolean-Wert und invertiert den Boolean-Wert so, dass er das Ergebnis widerspiegelt.

Alternativ können Sie dielastIndex-Eigenschaft einfach zurücksetzen:

result.Push(re.test('Foo Bar'));
re.lastIndex = 0;
result.Push(re.test('Foo Bar'));
34
James

Wenn Sie das globale Flag g entfernen, wird Ihr Problem behoben.

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

Sollte sein

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

Ich hatte die Funktion:

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

Der erste Anruf funktioniert .. .. Der zweite Anruf funktioniert nicht. Die slice-Operation beschwert sich über einen Nullwert. Ich gehe davon aus, dass dies am re.lastIndex liegt. Dies ist merkwürdig, da ich davon ausgehen würde, dass jedes Mal, wenn die Funktion aufgerufen wird, eine neue Variable RegExp zugewiesen wird, die nicht für mehrere Aufrufe meiner Funktion freigegeben ist.

Wenn ich es geändert habe:

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

Dann bekomme ich den lastIndex Holdover-Effekt nicht. Es funktioniert so, wie ich es erwarten würde.

0
Chelmite

Wenn Sie das Flag/g verwenden, wird die Suche nach einem Treffer fortgesetzt.

Wenn die Übereinstimmung erfolgreich ist, gibt die Methode exec () ein Array zurück und aktualisiert die Eigenschaften des regulären Ausdrucks.

Vor Ihrer ersten Suche:

myRegex.lastIndex
//is 0

Nach der ersten Suche

myRegex.lastIndex
//is 8

Entferne das g und beendet die Suche nach jedem Aufruf von exec ().