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).
Le problème est vraiment simple. Cela va le faire:
/^(?!.*foo\.htm$).*\.htm$/i
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
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.
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)
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:
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
);
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.