web-dev-qa-db-fra.com

Pourquoi un RegExp avec indicateur global donne-t-il des résultats erronés?

Quel est le problème avec cette expression régulière lorsque j'utilise le drapeau global et le drapeau insensible à la casse? La requête est une entrée générée par l'utilisateur. Le résultat devrait être [vrai, vrai].

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

L'objet RegExp garde une trace du lastIndex où une correspondance s'est produite. Par conséquent, lors des correspondances suivantes, il commencera à partir du dernier index utilisé, au lieu de 0. Regardez:

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

Si vous ne souhaitez pas réinitialiser manuellement lastIndex à 0 après chaque test, supprimez simplement l'indicateur g.

Voici l'algorithme dicté par les spécifications (section 15.10.6.2):

RegExp.prototype.exec (chaîne)

Effectue une correspondance d'expression régulière de chaîne contre l'expression régulière et retourne un objet Array contenant le résultats de la correspondance, ou null si le chaîne ne correspond pas à la chaîne ToString (string) est recherché pour un occurrence de l'expression régulière motif comme suit:

  1. Soit S la valeur de ToString (chaîne).
  2. Soit longueur la longueur de S.
  3. Soit lastIndex la valeur de la propriété lastIndex.
  4. Soit i la valeur de ToInteger (lastIndex).
  5. Si la propriété globale est false, laissez i = 0.
  6. Si je <0 ou I> longueur, définissez lastIndex sur 0 et renvoie la valeur null.
  7. Appelez [[Match]] en lui donnant les arguments S et i. Si [[Match]] échec retourné, passez à l’étape 8; sinon, r est le résultat final et passez à l'étape 10.
  8. Soit i = i + 1.
  9. Passez à l'étape 6.
  10. Soit e la valeur endIndex de r.
  11. Si la propriété globale a la valeur true, définissez lastIndex sur e.
  12. Soit n la longueur du tableau de capture de r. (Il s'agit de la même valeur Que celle de 15.10.2.1 NCapturingParens.)
  13. Renvoie un nouveau tableau avec les propriétés suivantes:
    • L'index propriété est définie sur la position du correspondance de sous-chaîne dans la totalité chaîne S.
    • La propriété d'entrée est définie à S.
    • La propriété length est définie sur n + 1.
    • La propriété 0 est définie sur le correspondance de sous-chaîne (c'est-à-dire la partie de S entre l'offset i inclus et l'offset e exclusif).
    • Pour chaque entier i tel que I> 0 et I ≤ n, définissez la propriété nommée ToString (i) sur le ième élément de r capture le tableau.
289
Ionuț G. Stan

Vous utilisez un seul objet RegExp et l'exécutez plusieurs fois. A chaque exécution successive, il continue à partir du dernier index de correspondance.

Vous devez "réinitialiser" l'expression rationnelle pour qu'elle recommence depuis le début avant chaque exécution:

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

Cela dit, il est peut-être plus facile de créer un nouvel objet RegExp à chaque fois (la surcharge est minime, car RegExp est de toute façon mis en cache):

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

RegExp.prototype.testmet à jour les expressions régulières 'lastIndexproperty afin que chaque test commence à l'endroit où le dernier s'est arrêté. Je suggérerais d'utiliserString.prototype.matchpuisqu'il ne met pas à jour la propriétélastIndex:

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

Remarque:!!le convertit en un booléen, puis inverse le booléen afin qu’il reflète le résultat.

Sinon, vous pouvez simplement réinitialiser la propriétélastIndex:

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

La suppression de l'indicateur global g résoudra votre problème.

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

Devrait être

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

J'ai eu la fonction:

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

Le premier appel fonctionne ... Le second appel ne fonctionne pas. L'opération slice se plaint d'une valeur nulle. Je suppose que cela est dû au re.lastIndex. C'est étrange car je m'attendrais à ce qu'une nouvelle RegExp soit allouée chaque fois que la fonction est appelée et ne soit pas partagée entre plusieurs appels de ma fonction.

Quand je l'ai changé pour:

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

Ensuite, je ne reçois pas l’effet de retenue lastIndex. Cela fonctionne comme je l’attendais.

0
Chelmite

Utiliser le drapeau/g lui dit de continuer à chercher après un hit.

Si la correspondance réussit, la méthode exec () renvoie un tableau et met à jour les propriétés de l'objet d'expression régulière.

Avant votre première recherche:

myRegex.lastIndex
//is 0

Après la première recherche

myRegex.lastIndex
//is 8

Supprimez le g et il quitte la recherche après chaque appel à exec ().

0