Reputation: 5291
I'm almost embarrassed to ask, but just almost. I seem to have an expression that works already, but it only seem to work in various regex tools like Regulator or online tools. Once in my C# code, it fails to hit where it should.
So the issue is that I need the regex to hit on user agent strings containing "android", "iphone" or "ipod", but not those also containing "opera". Simple enough, right?
So far, I've got this:
^(?=.*?\b(android|iphone|ipod)\b)(?!opera).*$
I'm using following options
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline
Please tell me where I got it wrong.
Here are some user agent strings. The expression should trigger on first and last, while ignore the two in the middle:
Mozilla/5.0+(Linux;+U;+Android+2.2;+nb-no;+Nexus+One+Build/FRF91)+AppleWebKit/533.1+(KHTML,+like Gecko)+Version/4.0+Mobile+Safari/533.1
Opera/9.80 (Android; Linux; Opera Mobi/ADR-1012221546; U; pl) Presto/2.7.60 Version/10.5
Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja)Presto/2.4.15
Mozila/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Geckto) Version/3.0 Mobile/3A101a Safari/419.3
Thanks
Upvotes: 5
Views: 4663
Reputation: 2104
Have you remembered to add a "@" in front of the pattern string? If not, the "\b" will be interpreted as a backspace.
Also as some say, you are not checking for Opera anywhere but in the beginning of the line, is that on purpose? I would probably use this syntax instead:
@"^(?!opera)(?=.*\b(android|iphone|ipod)\b).*$"
Upvotes: 5
Reputation: 118
Negative look-aheads are tricky. In short, you're only rejecting strings that do not contain opera at the start of the string (corrected, thanks @stema). Honestly, to keep from having the confusion often associated with look-ahead, you might want to break this into two parts:
Option 1:
Use the regex without lookahead, plus String.Contains()
Regex myRegex = "(?i)(android|iphone|ipod)"; //case insensitive
bool isUserStringNonOperaMobile = myRegex.isMatch(userString) && !userString.ToLower().Contains("opera");
Option 2:
Use two regexes
Regex myRegex = "(?i)(android|iphone|ipod)"; //case insensitive
Regex myOtherRegex = "(?i)opera"; // case insensitive
bool isUserStringNonOperaMobile = myRegex.isMatch(userString) && !myOtherRegex.isMatch(userString);
Yes, these might be slightly less efficient, but its negligible, and what you lose in efficiency, you gain in readability and maintainability.
Upvotes: 1
Reputation: 92996
You are checking for opera
only at the very beginning of the string. Is that intended?
Every look ahead tries to match from the start. You bind it to ^
and then your lookahead is only opera
, no other characters allowed before.
Try
^(?=.*?\b(android|iphone|ipod)\b)(?!.*opera).*$
to check for opera
somewhere in the string.
Upvotes: 1
Reputation: 26753
I'm not familiar with c# but are you sure it supports the ?!
construct?
Upvotes: -1