Regex negative lookbehinds with wildcard

I am trying to match some text if there is no other block of text in it. For example, I would like to match "bar" if "foo" does not precede it. I can match "bar" if "foo" doesn't immediately precede it using negative appearance in this regex:

 /(?<!foo)bar/ 

but I also like not to match "foo 12345 bar" . I tried:

 /(?<!foo.{1,10})bar/ 

but using a wildcard + range seems like an invalid regular expression in Ruby. I think the problem is wrong?

+7
source share
2 answers

You think about it right. But unfortunately, lookbehind usually have a fixed length. The only major exception is the .NET regex mechanism, which allows repeat quantizers inside lookbehinds. But since you only need a negative look, not a look. There is a hack for you. Cancel the line, then try matching:

 /rab(?!.{0,10}oof)/ 

Then change the result of the match or subtract the match position from the length of the string if that is what you are after.

Now, from the regex that you specified, I suggest that this was just a simplified version of what you really need. Of course, if bar is a complex pattern, another thought should decide how to cancel it correctly.

Note that if your template requires both variable and variable-length lookaheads, it will be harder for you to solve this. In addition, in your case it will be possible to deconstruct your lookbehind into several variable lengths (because you are not using either + or * ):

 /(?<!foo)(?<!foo.)(?<!foo.{2})(?<!foo.{3})(?<!foo.{4})(?<!foo.{5})(?<!foo.{6})(?<!foo.{7})(?<!foo.{8})(?<!foo.{9})(?<!foo.{10})bar/ 

But that’s not all that nice, is it?

+9
source

As m.buettner already mentioned, the lookbehind in Ruby regex must be of a fixed length and described in the document. Thus, you cannot put a quantifier in lookbehind.

You do not need to check everything in one step. Try a few regex matching steps to get what you want. Assuming that the existence of foo in front of a single instance of bar violates the condition, regardless of whether there is another bar , then

 string.match(/bar/) and !string.match(/foo.*bar/) 

will provide you with what you want as an example.

If you prefer the match to succeed with bar foo bar , you can do this

 string.scan(/foo|bar/).first == "bar" 
+3
source

All Articles