(?!$)
Is a negative lookahead stipulation, where (?!)
declares the negative lookahead and $
is what that the expression is "looking ahead" for (in this case, an end anchor).
A negative lookahead is an inverse of a positive lookahead, so it will be more intuitive to understand if you know what a positive lookahead is first: A digit followed by a positive lookahead \d(?=$)
basically looks for anything that would be matched by \d$
but does not return the part inside the lookahead stipulation when returning a match. \d(?=$)
will match any digit that is directly behind the end of the string. A negative lookahead will simply match every digit that is NOT directly behind the end of the string instead, ergo using \d(?!$)
and replacing matches with a * basically turns every digit in the string into a *
except for the last one.
For the sake of being thorough, you should know that (?<=)
is a positive lookbehind that looks for matches in the characters immediately before the given token instead of after, and (?<!)
is a negative lookbehind.
Regex101.com and RegExr.com are fantastic resources to use when you are learning regex, because you can insert a regular expression you don't understand and get a piece-by-piece explanation of an expression you don't understand and test strings in real time to experiment with what the expression captures and what it doesn't. Even if the built-in explanations don't make sense, you can still use them in situations like this to find out what something is called so you can search for it.