danijels
danijels

Reputation: 5291

User agent regular expression

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

Answers (4)

Johny Skovdal
Johny Skovdal

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

Greg Jackson
Greg Jackson

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

stema
stema

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

Ariel
Ariel

Reputation: 26753

I'm not familiar with c# but are you sure it supports the ?! construct?

Upvotes: -1

Related Questions