48

My problem is that i want to check the browserstring with pure regex.

Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13

-> should match

Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; Nexus One Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 

should not match

my tried solution is: /?((?<=Android)(?:[^])*?(?=Mobile))/i but it matches exactly wrong.

Right leg
  • 14,916
  • 6
  • 44
  • 75
user1061688
  • 481
  • 1
  • 4
  • 3

3 Answers3

91

You use look ahead assertions to check if a string contains a word or not.

If you want to assure that the string contains "Android" at some place you can do it like this:

^(?=.*Android).*

You can also combine them, to ensure that it contains "Android" at some place AND "Mobile" at some place:

^(?=.*Android)(?=.*Mobile).*

If you want to ensure that a certain word is NOT in the string, use the negative look ahead:

^(?=.*Android)(?!.*Mobile).*

This would require the word "Android to be in the string and the word "Mobile" is not allowed in the string. The .* part matches then the complete string/row when the assertions at the beginning are true.

See it here on Regexr

stema
  • 85,585
  • 19
  • 101
  • 125
  • +1 for the simplest pure-regex approach, but I'd write it as `^(?=.*Android)(?!.*Mobile).*`. Otherwise, the lookaheads will be applied at every position or until they both succeed. Your regexes work as expected because the positive lookaheads always fail everywhere but the beginning, but what if you have only negative lookaheads? – Alan Moore Nov 23 '11 at 12:55
  • @AlanMoore thanks for the suggestion, I edited my answer using it. – stema Nov 23 '11 at 13:14
  • @Stema Thanks for the great Explanation. Learnt something new today. – Vinit Divekar Jun 07 '18 at 23:47
  • Дай тебе бог здоровья! – Xotabu4 Dec 04 '19 at 09:29
3

With some implementations of regular expressions, you can use a negative lookbehind assertion. Per the docs, a negative lookbehind written as (?<!...) matches only if the current position in the string is not preceded by a match for ...

Here's a Python interactive script showing how to use negative lookbehind with your sample strings:

>>> s = "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13"
>>> bool(re.search(r'Android.*(?<! Mobile) Safari', s))
True

>>> t = "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; Nexus One Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
>>> bool(re.search(r'Android.*(?<! Mobile) Safari', t))
False
Raymond Hettinger
  • 199,887
  • 59
  • 344
  • 454
1

I'd just break it up

if ((m/Android/i) && (m/Safari/i) && !(m/Mobile Safari/i))

That said, depending on regex flaviour, you could combine that

if ((m/Android/i) && (m/(?<!Mobile )Safari/i))

or even

if (m/Android.*(?<!Mobile )Safari/i)

FYI see Lookahead/lookbehind


Update Tested these fine with Perl5 regex flavour (arguably the most popular flavour):

perl -ne 'print "$. yes\n" if m/Android.*(?<!Mobile )Safari/i'

Shows:

1 yes

for the given input in the OP

sehe
  • 350,152
  • 45
  • 431
  • 590