Reputation: 603
What is the fastest way to check if a string matches a certain pattern? Is regex the best way?
For example, I have a bunch of strings and want to check each one to see if they are a valid IP address (valid in this case meaning correct format), is the fastest way to do this using regex? Or is there something faster with like string formatting or something.
Something like this is what I have been doing so far:
for st in strs:
if re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', st) != None:
print 'IP!'
Upvotes: 60
Views: 154092
Reputation: 837926
The original answer bellow is good for 2011, but since 2012, one is likely better using Python's ipaddress stdlib module - besides checking IP validity for IPv4 and IPv6, it can do a lot of other things as well.
It looks like you are trying to validate IP addresses. A regular expression is probably not the best tool for this.
If you want to accept all valid IP addresses (including some addresses that you probably didn't even know were valid) then you can use IPy (Source):
from IPy import IP
IP('127.0.0.1')
If the IP address is invalid it will throw an exception.
Or you could use socket
(Source):
import socket
try:
socket.inet_aton(addr)
# legal
except socket.error:
# Not legal
If you really want to only match IPv4 with 4 decimal parts then you can split on dot and test that each part is an integer between 0 and 255.
def validate_ip(s):
a = s.split('.')
if len(a) != 4:
return False
for x in a:
if not x.isdigit():
return False
i = int(x)
if i < 0 or i > 255:
return False
return True
Note that your regular expression doesn't do this extra check. It would accept 999.999.999.999
as a valid address.
Upvotes: 106
Reputation: 752
from ipaddress import ip_address, IPv4Address
def validIPAddress(IP: str) -> str:
try:
return "IPv4" if type(ip_address(IP)) is IPv4Address else "IPv6"
except ValueError:
return "Invalid"
if __name__ == '__main__' :
# Enter the Ip address
Ip = "192.168.0.1"
print(validIPAddress(Ip))
Ip = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
print(validIPAddress(Ip))
Ip = "256.32.555.5"
print(validIPAddress(Ip))
Ip = "250.32:555.5"
print(validIPAddress(Ip))
Output :
IPv4
IPv6
Invalid
Invalid
Upvotes: 0
Reputation: 2858
I needed a solution for IPV4 addresses on Python 2.7 (old project at work)
socket.inet_aton
is more permissive than I'd like.This works for me:
def is_ipv4_address(ip_string):
ip_parts = ip_string.split('.')
return len(ip_parts) == 4 and all(part.isdigit() for part in ip_parts) and all(255 >= int(part) >=0 for part in ip_parts)
int(part) in range(0,255)
looks nicer than 255 >= int(part) >=0
, but is slower:%timeit 5 in range(0,255)
113 ns ± 1.27 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
%timeit 255 >= 5 >= 0
30.5 ns ± 0.276 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
ipaddress.ip_address()
:import ipaddress
ip = '192.168.0.0'
%timeit ipaddress.ip_address(ip)
2.15 µs ± 21.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
%timeit is_ipv4_address(ip)
1.18 µs ± 24.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Upvotes: 1
Reputation: 7636
In Python 3.* is very simple, this is a utily function that will check for any ip, ipv4 or ipv6 , that's just using the Python Standard Library ipaddress — IPv4/IPv6 manipulation library
from ipaddress import ip_address, IPv4Address, IPv6Address, AddressValueError
def _is_valid_ip_address(ip, ipv_type: str = 'any') -> bool:
"""Validates an ipd address"""
try:
if ipv_type == 'any':
ip_address(ip)
elif ipv_type == 'ipv4':
IPv4Address(ip)
elif ipv_type == 'ipv6':
IPv6Address(ip)
else:
raise NotImplementedError
except (AddressValueError, ValueError):
return False
else:
return True
def run_tests():
ipv4 = '192.168.0.1'
ipv6 = '2001:db8::1000'
bad = "I AM NOT AN IP"
is_pv4 = _is_valid_ip_address(ipv4)
is_pv6 = _is_valid_ip_address(ipv6)
bad_ip = _is_valid_ip_address(bad)
am_i_pv4 = _is_valid_ip_address(ipv6, ipv_type='ipv4')
am_i_pv6 = _is_valid_ip_address(ipv4, ipv_type='ipv6')
print(f'''
* is_pv4 -> {is_pv4}
* is_pv6 -> {is_pv6}
* bad_ip -> {bad_ip}
* am_i_pv4 -> {am_i_pv4}
* am_i_pv6 -> {am_i_pv6}
''')
if __name__ == '__main__':
run_tests()
The result
* is_pv4 -> True
* is_pv6 -> True
* bad_ip -> False
* am_i_pv4 -> False
* am_i_pv6 -> False
Upvotes: 1
Reputation: 113
iptools can be used.
import iptools
ipv4 = '1.1.1.1'
ipv6 = '5000::1'
iptools.ipv4.validate_ip(ipv4) #returns bool
iptools.ipv6.validate_ip(ipv6) #returns bool
Upvotes: 2
Reputation: 881487
I'm normally the one of the very few Python experts who steadfastly defends regular expressions (they have quite a bad reputation in the Python community), but this is not one of those cases -- accepting (say) '333.444.555.666'
as an "IP address" is really bad, and if you need to do more checks after matching the RE, much of the point of using a RE is lost anyway. So, I second @Mark's recommendations heartily: IPy for generality and elegance (including support of IPv6 if you want!), string operations and int checks if you only need IPv4 (but, think twice about that limitation, and then think one more -- IPv6's time has way come!-):
def isgoodipv4(s):
pieces = s.split('.')
if len(pieces) != 4: return False
try: return all(0<=int(p)<256 for p in pieces)
except ValueError: return False
I'd far rather do that than a convoluted RE to match only numbers between 0 and 255!-)
Upvotes: 14
Reputation: 1
You may try the following (the program can be further optimized):
path = "/abc/test1.txt"
fh = open (path, 'r')
ip_arr_tmp = []
ip_arr = []
ip_arr_invalid = []
for lines in fh.readlines():
resp = re.search ("([0-9]+).([0-9]+).([0-9]+).([0-9]+)", lines)
print resp
if resp != None:
(p1,p2,p3,p4) = [resp.group(1), resp.group(2), resp.group(3), resp.group(4)]
if (int(p1) < 0 or int(p2) < 0 or int(p3) < 0 or int(p4) <0):
ip_arr_invalid.append("%s.%s.%s.%s" %(p1,p2,p3,p4))
elif (int(p1) > 255 or int(p2) > 255 or int(p3) > 255 or int(p4) > 255):
ip_arr_invalid.append("%s.%s.%s.%s" %(p1,p2,p3,p4))
elif (len(p1)>3 or len(p2)>3 or len(p3)>3 or len(p4)>3):
ip_arr_invalid.append("%s.%s.%s.%s" %(p1,p2,p3,p4))
else:
ip = ("%s.%s.%s.%s" %(p1,p2,p3,p4))
ip_arr_tmp.append(ip)
print ip_arr_tmp
for item in ip_arr_tmp:
if not item in ip_arr:
ip_arr.append(item)
print ip_arr
Upvotes: 0
Reputation: 191
On Python 3.6 I think is much simpler as ipaddress module is already included:
import ipaddress
def is_ipv4(string):
try:
ipaddress.IPv4Network(string)
return True
except ValueError:
return False
Upvotes: 17
Reputation: 6283
#!/usr/bin/python
import sys
def check_ip(address):
part=address.split(".")
temp=True
if len(part) != 4:
temp=False
return temp
for p in part:
if not 0<= int(p) <= 255:
temp=False
return temp
else:
temp=True
return temp
if __name__=="__main__":
print check_ip(sys.argv[1])
Save the code with some name say- check_ip.py
and run it as python check_ip.py 192.168.560.25
Note:- Above code fails for the below ip address-
023.65.029.33
Upvotes: 0
Reputation: 603
Other regex answers in this page will accept an IP with a number over 255.
This regex will avoid this problem:
import re
def validate_ip(ip_str):
reg = r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
if re.match(reg, ip_str):
return True
else:
return False
Upvotes: 3
Reputation: 363
We do not need any import to do this. This also works much faster
def is_valid_ip(str_ip_addr):
"""
:return: returns true if IP is valid, else returns False
"""
ip_blocks = str(str_ip_addr).split(".")
if len(ip_blocks) == 4:
for block in ip_blocks:
# Check if number is digit, if not checked before calling this function
if not block.isdigit():
return False
tmp = int(block)
if 0 > tmp > 255:
return False
return True
return False
Upvotes: 0
Reputation: 173
This works for ipv6 addresses as well.
Unfortunately it Works for python3 only
import ipaddress
def valid_ip(address):
try:
print ipaddress.ip_address(address)
return True
except:
return False
print valid_ip('10.10.20.30')
print valid_ip('2001:DB8::1')
print valid_ip('gibberish')
Upvotes: 0
Reputation: 2933
Very simple to check whether given IP is valid or not using in built library ipaddress. You can also validate using mask value.
ip = '30.0.0.1' #valid
#ip = '300.0.0.0/8' #invalid
#ip = '30.0.0.0/8' #valid
#ip = '30.0.0.1/8' #invalid
#ip = 'fc00:da00::3402:69b1' #valid
#ip = 'fc00:da00::3402:69b1/128' #valid
#ip = 'fc00:da00::3402:69b1:33333' #invalid
if ip.find('/') > 0:
try:
temp2 = ipaddress.ip_network(ip)
print('Valid IP network')
except ValueError:
print('Invalid IP network, value error')
else:
try:
temp2 = ipaddress.ip_address(ip)
print('Valid IP')
except ValueError:
print('Invalid IP')
Note: Tested in Python 3.4.3
Upvotes: 1
Reputation: 2537
Install netaddr package
sudo pip install netaddr
And then you can do this
>>> from netaddr import valid_ipv4
>>> valid_ipv4('11.1.1.2')
True
>>> valid_ipv4('11.1.1.a')
False
Also you create a IPAddress object from that string and a lot more ip related operations
>>> from netaddr import IPAddress
>>> ip = IPAddress('11.1.1.1')
>>> [f for f in dir(ip) if '__' not in f]
['_module', '_set_value', '_value', 'bin', 'bits', 'format', 'info', 'ipv4', 'ipv6', 'is_hostmask', 'is_ipv4_compat', 'is_ipv4_mapped', 'is_link_local', 'is_loopback', 'is_multicast', 'is_netmask', 'is_private', 'is_reserved', 'is_unicast', 'key', 'netmask_bits', 'packed', 'reverse_dns', 'sort_key', 'value', 'version', 'words']
Upvotes: 2
Reputation: 823
If you use Python3, you can use ipaddress
module http://docs.python.org/py3k/library/ipaddress.html. Example:
>>> import ipaddress
>>> ipv6 = "2001:0db8:0a0b:12f0:0000:0000:0000:0001"
>>> ipv4 = "192.168.2.10"
>>> ipv4invalid = "266.255.9.10"
>>> str = "Tay Tay"
>>> ipaddress.ip_address(ipv6)
IPv6Address('2001:db8:a0b:12f0::1')
>>> ipaddress.ip_address(ipv4)
IPv4Address('192.168.2.10')
>>> ipaddress.ip_address(ipv4invalid)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/python3.4/ipaddress.py", line 54, in ip_address
address)
ValueError: '266.255.9.10' does not appear to be an IPv4 or IPv6 address
>>> ipaddress.ip_address(str)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/python3.4/ipaddress.py", line 54, in ip_address
address)
ValueError: 'Tay Tay' does not appear to be an IPv4 or IPv6 address
Upvotes: 61
Reputation: 1232
I cheated and used combination of multiple answers submitted by other people. I think this is pretty clear and straight forward piece of code. ip_validation
should return True
or False
. Also this answer only works for IPv4 addresses
import re
ip_match = re.match('^' + '[\.]'.join(['(\d{1,3})']*4) + '$', ip_input)
ip_validate = bool(ip_match)
if ip_validate:
ip_validate &= all(map(lambda n: 0 <= int(n) <= 255, ip_match.groups())
Upvotes: 0
Reputation: 5555
One more validation without re:
def validip(ip):
return ip.count('.') == 3 and all(0<=int(num)<256 for num in ip.rstrip().split('.'))
for i in ('123.233.42.12','3234.23.453.353','-2.23.24.234','1.2.3.4'):
print i,validip(i)
Upvotes: 6
Reputation: 992707
Your regular expression doesn't check for the end of the string, so it would match:
123.45.67.89abc123boogabooga
To fix this, use:
'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
(note the $
at the end).
Finally, in Python the usual style is to use is not None
instead of != None
.
Upvotes: 3
Reputation: 7285
If you are validating IP address I would suggest the following:
import socket
try:
socket.inet_aton(addr)
return True
except socket.error:
return False
If you just want to check if it is in the right format then you would want to do it for all legal bases (not just base 10 numbering).
Also, are the IP address IPv4 only (and none are IPv6) then you could just look up what valid address are and use split()
(to get individual components of the IP) and int()
(to type-caste for comparison). A quick reference to valid IPv4 rules is here.
Upvotes: 2
Reputation: 19895
you should precompile the regexp, if you use it repeatedly
re_ip = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
# note the terminating $ to really match only the IPs
then use
if re_ip.match(st):
print '!IP'
but.. is e.g. '111.222.333.444' really the IP?
i'd look at netaddr
or ipaddr
libraries whether they can be used to match IPs
Upvotes: 0
Reputation: 40193
You can make it a little faster by compiling it:
expression = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
for st in strs:
if expression.match(st):
print 'IP!'
Upvotes: 0