user606521
user606521

Reputation: 15434

why this regexp returns match?

http://jsfiddle.net/sqee98xr/

var reg = /^(?!managed).+\.coffee$/
var match = '20150212214712-test-managed.coffee'.match(reg)
console.log(match) // prints '20150212214712-test-managed.coffee'

I want to match regexp only if there is not word "managed" present in a string - how I can do that?

Upvotes: 1

Views: 58

Answers (2)

Ryan Wheale
Ryan Wheale

Reputation: 28410

Negative lookaheads are weird. You have to match more than just the word you are looking for. It's weird, I know.

var reg = /^(?!.*managed).+\.coffee$/

http://jsfiddle.net/sqee98xr/3/

EDIT: It seems I really got under some people's skin with the "weird" descriptor and lay description. It's weird because on a surface level the term "negative lookahead" implies "look ahead and make sure the stuff in these parenthesis isn't up there, then come back and continue matching". As a lover of regex, I still proclaim this naming is weird, especially to first time users of the assertion. To me it's easier to think of it as a "not" operator as opposed to something which actually crawls forward and "looks ahead". In order to get behavior to resemble an actual "look ahead", you have to match everything before the search term, hence the .*.

An even easier solution would have been to remove the start-of-string (^) assertion. Again, to me it's easier to read ?! as "not".

var reg = /(?!managed).+\.coffee$/

Upvotes: 1

Spencer Wieczorek
Spencer Wieczorek

Reputation: 21575

While @RyanWheale's solution is correct, the explanation isn't correct. The reason essentially is that a string that contains the word "managed" (such as "test-managed" ) can count as not "managed". To get an idea of this first lets look at the regular expression:

/^(?!managed).+\.coffee$/ 

// (Not "managed")(one or more characters)(".")("coffee")

So first we cannot have a string with the text "managed", then we can have one or more characters, then a dot, followed by the text "coffee". Here is an example that fulfills this.

"Hello.coffee" [ PASS ]

Makes sense, "Hello" certainly is not "managed". Here is another example that works from your string:

"20150212214712-test-managed.coffee" [ PASS ]

Why? Because "20150212214712-test-managed" is not the string "managed" even though it contains the string, the computer does not know that's what you mean. It thinks that "20150212214712-test-managed" as a string that isn't "managed" in the same way "andflaksfj" isn't "managed". So the only way it fails is if "managed" was at the start of the string:

"managed.coffee" [ FAIL ]

This isn't just because the text "managed" is there. Say the computer said that "managed." was not "managed". It would indeed pass the (?!managed) part but the rest of the string would just be coffee and it would fail because there is no ".".

Finally the solution to this is as suggested by the other answer:

/^(?!.*managed).+\.coffee$/

Now the string "20150212214712-test-managed.coffee" fails because no matter how it's looked at: "test-managed", "-managed", "st-managed", etc. Would still count as (?!.*managed) and fail. As in the example above this one it could try adding a sub-string from ".coffee", but as explained this would cause the string to fail in the rest of the regexp ( .+\.coffee$ ).

Hopefully this long explanation explained that Negative look-aheads are not weird, just takes your request very literally.

Upvotes: 1

Related Questions