web-dev-qa-db-fra.com

LookAead négatif Expression régulière

Je veux faire correspondre toutes les chaînes se terminant par ".htm" sauf si elle se termine par "foo.htm". Je suis généralement décent, avec des expressions régulières, mais les questions négatives me déconcertent. Pourquoi ça ne marche pas?

/(?!foo)\.htm$/i.test("/foo.htm");  // returns true. I want false.

Que devrais-je utiliser à la place? Je pense que j'ai besoin d'un "regard négatifderrière"expression (si JavaScript supportait une telle chose, ce que je sais, ce n’est pas le cas).

56
gilly3

Le problème est vraiment simple. Cela va le faire:

/^(?!.*foo\.htm$).*\.htm$/i

92
ridgerunner

Ce que vous décrivez (votre intention) est un négatif look-behind , et Javascript ne prend pas en charge les look-behind.

Les personnages futurs dans lesquels ils ont été placés attendent avant - le .. Donc, ce que vous avez en fait dit "tout ce qui se termine par .htm tant que les trois premiers caractères commençant à cette position (.ht) ne sont pas foo", ce qui est toujours vrai.

Habituellement, le substitut des regards négatifs consiste à en faire correspondre plus que nécessaire et à extraire uniquement la partie dont vous avez réellement besoin. C'est hacky, et selon votre situation précise, vous pouvez probablement trouver autre chose, mais quelque chose comme ceci:

// Checks that the last 3 characters before the dot are not foo:
/(?!foo).{3}\.htm$/i.test("/foo.htm"); // returns false 
18
Nicole

Comme mentionné, JavaScript ne prend pas en charge les assertions de regard négatif.

Mais vous pouvez utiliser un workaroud:

/(foo)?\.htm$/i.test("/foo.htm") && RegExp.$1 != "foo";

Cela correspondra à tout ce qui se termine par .htm mais il stockera "foo" dans RegExp.$1 s'il correspond à foo.htm, afin que vous puissiez le gérer séparément.

2
Floern

Comme Renesis l'a mentionné, "lookbehind" n'est pas supporté en JavaScript, utilisez donc peut-être simplement deux expressions régulières combinées:

!/foo\.htm$/i.test(teststring) && /\.htm$/i.test(teststring)
2
petho

Cette réponse est probablement arrivée un peu plus tard que nécessaire, mais je la laisserai ici au cas où quelqu'un rencontrerait le même problème maintenant (7 ans et 6 mois après avoir posé cette question).

Désormais, les révisions sont incluses dans la norme ECMA2018 et sont prises en charge au moins dans la dernière version de Chrome. Cependant, vous pouvez résoudre le puzzle avec ou sans eux.

Une solution avec une anticipation négative:

let testString = `html.htm app.htm foo.tm foo.htm bar.js 1to3.htm _.js _.htm`;

testString.match(/\b(?!foo)[\w-.]+\.htm\b/gi);
> (4) ["html.htm", "app.htm", "1to3.htm", "_.htm"]

Une solution à la recherche négative:

testString.match(/\b[\w-.]+(?<!foo)\.htm\b/gi);
> (4) ["html.htm", "app.htm", "1to3.htm", "_.htm"]

Une solution avec un look (techniquement) positif:

testString.match(/\b(?=[^f])[\w-.]+\.htm\b/gi);
> (4) ["html.htm", "app.htm", "1to3.htm", "_.htm"]

etc.

Tous ces RegExps disent au moteur JS la même chose de différentes manières, le message qu’ils transmettent au moteur JS ressemble à peu près au suivant.

Veuillez trouver dans cette chaîne toutes les séquences de caractères qui sont:

  • Séparé de tout autre texte (comme des mots);
  • Se compose d’une ou de plusieurs lettres d’alphabet anglais, de soulignement (s) de trait, Trait (s) à trait, point (s) ou chiffre (s);
  • Terminez avec ".htm";
  • En dehors de cela, la partie de la séquence avant ".htm" pourrait être n'importe quoi Sauf "foo".
1
Igor Bykov

String.prototype.endsWith (ES6)

console.log( /* !(not)endsWith */

    !"foo.html".endsWith("foo.htm"), // true
  !"barfoo.htm".endsWith("foo.htm"), // false (here you go)
     !"foo.htm".endsWith("foo.htm"), // false (here you go)
   !"test.html".endsWith("foo.htm"), // true
    !"test.htm".endsWith("foo.htm")  // true

);

1
Roko C. Buljan

Vous pouvez émuler le lookbehind négatif avec quelque chose comme /(.|..|.*[^f]..|.*f[^o].|.*fo[^o])\.htm$/, mais une approche programmatique serait préférable.

0
ngn