简体   繁体   中英

Negative lookahead Regular Expression

I want to match all strings ending in ".htm" unless it ends in "foo.htm". I'm generally decent with regular expressions, but negative lookaheads have me stumped. Why doesn't this work?

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

What should I be using instead? I think I need a "negative look behind " expression (if JavaScript supported such a thing, which I know it doesn't).

The problem is pretty simple really. This will do it:

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

What you are describing (your intention) is a negative look-behind , and Javascript has no support for look-behinds.

Look-aheads look forward from the character at which they are placed — and you've placed it before the . . So, what you've got is actually saying "anything ending in .htm as long as the first three characters starting at that position ( .ht ) are not foo " which is always true.

Usually, the substitute for negative look-behinds is to match more than you need, and extract only the part you actually do need. This is hacky, and depending on your precise situation you can probably come up with something else, but something like this:

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

As mentioned JavaScript does not support negative look-behind assertions.

But you could use a workaroud:

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

This will match everything that ends with .htm but it will store "foo" into RegExp.$1 if it matches foo.htm , so you can handle it separately.

与Renesis提到的一样,JavaScript中不支持“lookbehind”,因此可能只使用两个正则表达式组合:

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

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

Probably this answer has arrived just a little bit later than necessary but I'll leave it here just in case someone will run into the same issue now (7 years, 6 months after this question was asked).

Now lookbehinds are included in ECMA2018 standard & supported at least in last version of Chrome. However, you might solve the puzzle with or without them.

A solution with negative lookahead:

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"]

A solution with negative lookbehind:

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

A solution with (technically) positive lookahead:

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

etc.

All these RegExps tell JS engine the same thing in different ways, the message that they pass to JS engine is something like the following.

Please, find in this string all sequences of characters that are:

  • Separated from other text (like words);
  • Consist of one or more letter(s) of english alphabet, underscore(s), hyphen(s), dot(s) or digit(s);
  • End with ".htm";
  • Apart from that, the part of sequence before ".htm" could be anything but "foo".

您可以使用类似/(.|..|.*[^f]..|.*f[^o].|.*fo[^o])\\.htm$/模拟负面的/(.|..|.*[^f]..|.*f[^o].|.*fo[^o])\\.htm$/ ,但是程序化方法会更好。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM