Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

This regex matches and shouldn't. Why is it?

Tags:

regex

This regex:

^((https?|ftp)\:(\/\/)|(file\:\/{2,3}))?(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3} (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(((([a-zA-Z0-9]+)(\.)?)+?)(\.)([a-z]{2} |com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum))([a-zA-Z0-9\?\=\&\%\/]*)?$

Formatted for readability:

^( # Begin regex / begin address clause
  (https?|ftp)\:(\/\/)|(file\:\/{2,3}))? # protocol
  ( # container for two address formats, more to come later
   ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
   (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) # match IP addresses
  )|( # delimiter for address formats
   ((([a-zA-Z0-9]+)(\.)?)+?) # match domains and any number of subdomains
   (\.) #dot for .com
   ([a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum) #TLD clause
  ) # end address clause
([a-zA-Z0-9\?\=\&\%\/]*)? # querystring support, will pretty this up later
$

is matching:

www.google

and shouldn't be. This is one of my "fail" test cases. I have declared the TLD portion of the URL to be mandatory when matching on alpha instead of on IP, and "google" doesn't fit into the "[a-z]{2}" clause.

Keep in mind I will fix the following issues seperately - this question is about why it matches www.google and shouldn't.

  • Querystring needs to support proper formats only, currently accepts any combination of querystring characters
  • Several protocols not supported, though the scope of my requirements may not include them
  • uncommon TLDs with 3 characters not included
  • Probably matches http://www.google..com - will check for consecutive dots
  • Doesn't support decimal IP address formats

What's wrong with my regex?

edit: See also a previous problem with an earlier version of this regex on a different test case: How can I make this regex match correctly?



edit2: Fixed - The corrected regex (as asked) is:

^((https?|ftp)\:(\/\/)|(file\:\/{2,3}))?(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3} (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(((([a-zA-Z0-9]+)(\.)?)+?)(\.)([a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum))([\/][\/a-zA-Z0-9\.]*)*?([\/]?[\?][a-zA-Z0-9\=\&\%\/]*)?$
like image 638
tsilb Avatar asked Oct 27 '09 09:10

tsilb


People also ask

What does '$' mean in regex?

$ means "Match the end of the string" (the position after the last character in the string). Both are called anchors and ensure that the entire string is matched instead of just a substring.

How do you say does not contain in regex?

In order to match a line that does not contain something, use negative lookahead (described in Recipe 2.16). Notice that in this regular expression, a negative lookahead and a dot are repeated together using a noncapturing group.

How do you stop greedy in regex?

You make it non-greedy by using ". *?" When using the latter construct, the regex engine will, at every step it matches text into the "." attempt to match whatever make come after the ". *?" . This means that if for instance nothing comes after the ".

How do you check if a regex matches a string?

If you need to know if a string matches a regular expression RegExp , use RegExp.prototype.test() . If you only want the first match found, you might want to use RegExp.prototype.exec() instead.


1 Answers

"google" might not fit in [a-z]{2}, but it does fit in [a-z]{2}([a-zA-Z0-9\?\=\&\%\/]*)? - you forgot to require a / after the TLD if the URL extends beyond the domain. So it's interpreting it with "www.go" as the domain and then "ogle" following it, with no slash in between. You can fix it by adding a [?/] to the front of that last group to require one of those two symbols between the TLD and any further portion of the URL.

like image 110
Amber Avatar answered Oct 21 '22 11:10

Amber