2008-11-25 20 views
121

Jaki jest najlepszy sposób sprawdzenia, czy adres IP wprowadzony przez użytkownika jest prawidłowy? Wchodzi jako ciąg.Jak sprawdzić poprawność adresu IP w Pythonie?

+0

Chcę tylko zwrócić uwagę, że jeśli adres rozgłoszeniowy nie jest uznawany za prawidłowy adres, to każde z proponowanych rozwiązań zawodzi. Musisz przetestować maskę podsieci, aby sprawdzić, czy jest to adres rozgłoszeniowy. – Bitt

Odpowiedz

5

myślę, że to zrobi to ...

def validIP(address): 
    parts = address.split(".") 
    if len(parts) != 4: 
     return False 
    for item in parts: 
     if not 0 <= int(item) <= 255: 
      return False 
    return True 
+2

Możesz chcieć złapać wyjątek ValueError z int() na wypadek, gdyby użytkownik wpisze "a.b.d", a nie liczby całkowite. –

+6

Zły kod, działa tylko z adresami IPv4. – bortzmeyer

+3

Python's int() przymus jest zbyt luźny; na przykład usuwa spacje. –

40
def is_valid_ip(ip): 
    """Validates IP addresses. 
    """ 
    return is_valid_ipv4(ip) or is_valid_ipv6(ip) 

IPv4:

def is_valid_ipv4(ip): 
    """Validates IPv4 addresses. 
    """ 
    pattern = re.compile(r""" 
     ^
     (?: 
      # Dotted variants: 
      (?: 
      # Decimal 1-255 (no leading 0's) 
      [3-9]\d?|2(?:5[0-5]|[0-4]?\d)?|1\d{0,2} 
      | 
      0x0*[0-9a-f]{1,2} # Hexadecimal 0x0 - 0xFF (possible leading 0's) 
      | 
      0+[1-3]?[0-7]{0,2} # Octal 0 - 0377 (possible leading 0's) 
     ) 
      (?:     # Repeat 0-3 times, separated by a dot 
      \. 
      (?: 
       [3-9]\d?|2(?:5[0-5]|[0-4]?\d)?|1\d{0,2} 
      | 
       0x0*[0-9a-f]{1,2} 
      | 
       0+[1-3]?[0-7]{0,2} 
      ) 
     ){0,3} 
     | 
      0x0*[0-9a-f]{1,8} # Hexadecimal notation, 0x0 - 0xffffffff 
     | 
      0+[0-3]?[0-7]{0,10} # Octal notation, 0 - 037777777777 
     | 
      # Decimal notation, 1-4294967295: 
      429496729[0-5]|42949672[0-8]\d|4294967[01]\d\d|429496[0-6]\d{3}| 
      42949[0-5]\d{4}|4294[0-8]\d{5}|429[0-3]\d{6}|42[0-8]\d{7}| 
      4[01]\d{8}|[1-3]\d{0,9}|[4-9]\d{0,8} 
     ) 
     $ 
    """, re.VERBOSE | re.IGNORECASE) 
    return pattern.match(ip) is not None 

IPv6:

def is_valid_ipv6(ip): 
    """Validates IPv6 addresses. 
    """ 
    pattern = re.compile(r""" 
     ^
     \s*       # Leading whitespace 
     (?!.*::.*::)    # Only a single whildcard allowed 
     (?:(?!:)|:(?=:))   # Colon iff it would be part of a wildcard 
     (?:       # Repeat 6 times: 
      [0-9a-f]{0,4}   # A group of at most four hexadecimal digits 
      (?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard 
     ){6}      # 
     (?:       # Either 
      [0-9a-f]{0,4}   # Another group 
      (?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard 
      [0-9a-f]{0,4}   # Last group 
      (?: (?<=::)    # Colon iff preceeded by exacly one colon 
      | (?<!:)    # 
      | (?<=:) (?<!::) : # 
      )      # OR 
     |       # A v4 address with NO leading zeros 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d) 
      (?: \. 
       (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d) 
      ){3} 
     ) 
     \s*       # Trailing whitespace 
     $ 
    """, re.VERBOSE | re.IGNORECASE | re.DOTALL) 
    return pattern.match(ip) is not None 

Wersja IPv6 wykorzystuje "(?:(?<=::)|(?<!::):)", który można zastąpić przez "(?(?<!::):) "w przypadku silników regex, które obsługują warunkowe z rozejrzanymi widokami. (Tj PCRE, .NET)

Edit:

  • Wypada rodzimą odmianę.
  • Rozszerzono wyrażenie regularne, aby zachować zgodność z dokumentem RFC.
  • Dodano kolejne wyrażenie do adresów IPv6.

Edit2:

znalazłem kilka linków omawianiu sposobu analizowania adresy IPv6 z regex:

Edit3:

Wreszcie udało się napisać wzór, który przechodzi wszystkie testy, a ja też jestem zadowolony.

+1

Nie, działa tylko z adresami IPv4. – bortzmeyer

+0

test-ipv6-regex.pl jest złoty +1 – Marcin

+0

To powinna być odpowiedź, @ chills42. –

153

Nie parsuj tego. Po prostu zapytaj.

import socket 

try: 
    socket.inet_aton(addr) 
    # legal 
except socket.error: 
    # Not legal 
+12

Hmm, zdaje się akceptować rzeczy takie jak "4" i "192.168" i cicho podkłada resztę zerami. Technicznie ważny, jestem pewien, ale nie do końca tego, czego się spodziewałem. – krupan

+1

Są to prawidłowe reprezentacje adresów IP. 127.1 to localhost, 1172703390 to mój serwer WWW. Jeśli chcesz upewnić się, że jest w kropkowanym kwadracie, możesz również sprawdzić, czy len (addr.split ('.')) == 4 – Dustin

+1

@krupan: możesz połączyć powyższe z testem dla "len (addr.split (" . ")) == 4" jeśli chcesz odrzucić krótsze adresy. –

45

IPy module (moduł przeznaczony do czynienia z adresami IP) rzuci wyjątek ValueError dla nieprawidłowych adresów.

>>> from IPy import IP 
>>> IP('127.0.0.1') 
IP('127.0.0.1') 
>>> IP('277.0.0.1') 
Traceback (most recent call last): 
... 
ValueError: '277.0.0.1': single byte must be 0 <= byte < 256 
>>> IP('foobar') 
Traceback (most recent call last): 
... 
ValueError: invalid literal for long() with base 10: 'foobar' 

Jednak, podobnie jak odpowiedź Dustina, będzie przyjmować takie rzeczy jak „4” i „192.168”, ponieważ, jak wspomniano, te obowiązują reprezentacje adresów IP.

Jeśli używasz Pythona 3.3 lub później, zawiera obecnie ipaddress module:

>>> import ipaddress 
>>> ipaddress.ip_address('127.0.0.1') 
IPv4Address('127.0.0.1') 
>>> ipaddress.ip_address('277.0.0.1') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python3.3/ipaddress.py", line 54, in ip_address 
    address) 
ValueError: '277.0.0.1' does not appear to be an IPv4 or IPv6 address 
>>> ipaddress.ip_address('foobar') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python3.3/ipaddress.py", line 54, in ip_address 
    address) 
ValueError: 'foobar' does not appear to be an IPv4 or IPv6 address 

dla Pythona 2, można uzyskać taką samą funkcjonalność korzystania adresip jeśli zainstalować python-adres_IP

pip install ipaddress 

Moduł ten jest kompatybilny z Pythonie 2 i zapewnia bardzo podobny interfejs API do modułu ipaddress zawartego w Python Standard Library od wersji Python 3.3. Więcej szczegółów here. W Pythonie 2 będziesz musiał jawnie przekonwertować ciąg adresu IP na kod Unicode: ipaddress.ip_address(u'127.0.0.1').

+0

Doskonały pomysł. Jedyne dotychczasowe rozwiązanie, które działa ze wszystkimi adresami IP. >>> od IPy import IP >>> IP ("2001: 660 :: 1") IP ('2001: 660 :: 1') – bortzmeyer

+0

Dla Pythona 2, zainstaluj pip ipad i otrzymasz prawie to samo API :) – radtek

+0

Odnośnie 'import ipaddress', kiedy przekazałem adres IPv4, otrzymałem wynik podobny do' IPv4Address ('127.0.0.1') '. Ale kiedy próbowałem przekonwertować go na 'string', aby sprawdzić, czy zawiera on' IPv4' lub 'IPv6', właśnie dostałem adres IP. Skąd mam wiedzieć w kodzie, czy jest to 'IPv4' lub' IPv6'? Czy 'if" IPv4 "w str (typ (val)):' dobry pomysł? – Gauranga

53
import socket 

def is_valid_ipv4_address(address): 
    try: 
     socket.inet_pton(socket.AF_INET, address) 
    except AttributeError: # no inet_pton here, sorry 
     try: 
      socket.inet_aton(address) 
     except socket.error: 
      return False 
     return address.count('.') == 3 
    except socket.error: # not a valid address 
     return False 

    return True 

def is_valid_ipv6_address(address): 
    try: 
     socket.inet_pton(socket.AF_INET6, address) 
    except socket.error: # not a valid address 
     return False 
    return True 
+0

Dlaczego linia: "return address.count ('.') == 3" ?? Czy to pozostało po debugowaniu? – quux

+8

@quux: nie. To długa dyskusja, a ludzie nie lubią tego, że przynajmniej skrócone adresy Linux i Windows są uznawane za dopuszczalne. Na przykład 'socket.inet_aton ('127.1')' zwraca wartość ''\ x7f \ x00 \ x00 \ x01'' (czyli dokładnie tak, jak" 127.0.0.1 "). Miałem tę męczącą i długotrwałą dyskusję gdzie indziej na SO, ale nie mogę sobie przypomnieć gdzie. – tzot

+0

Dzięki. Nie wiedziałem tego! – quux

25

Od Pythona 3.4, najlepszym sposobem, aby sprawdzić, czy adres IPv4 lub IPv6 jest poprawna, jest użycie modułu Python biblioteki standardowej ipaddress - IPv4/IPv6 biblioteki manipulacji S.A. https://docs.python.org/3/library/ipaddress.html dla pełnej dokumentacji.

przykład:

#!/usr/bin/env python 

import ipaddress 
import sys 

try: 
    ip = ipaddress.ip_address(sys.argv[1]) 
    print '%s is a correct IP%s address.' % (ip, ip.version) 
except ValueError: 
    print 'address/netmask is invalid: %s' % sys.argv[1] 
except: 
    print 'Usage : %s ip' % sys.argv[0] 

W innych wersjach Github, phihag/Philipp Hagemeister "Python 3,3 za adresip starszych wersji Pythona", https://github.com/phihag/ipaddress

backporty z phihag dostępne np w Anacondzie Python 2.7 & jest zawarty w Instalatorze. s.a. https://docs.continuum.io/anaconda/pkg-docs

Aby zainstalować z pip:

pip install ipaddress 

SA: adresip 1.0.17 "IPv4/IPv6 biblioteka manipulacja", "Port w 3.3+ modułu ipaddress" https://pypi.python.org/pypi/ipaddress/1.0.17

+1

Sugeruję, abyś edytował swoją odpowiedź określając wersję (wersje) Pythona, że ​​ten moduł jest dołączony do stdlib. – tzot

+1

@tzot ktos powinien rozwiazac Python 2.x i wszelkiego rodzaju fajne rzeczy do niego z 3.x bibliotek, a nawet rdzenia. MOIM ZDANIEM. –

+0

Otrzymuję ten błąd 'C: \ Python \ Codes> check_ip.py Plik" C: \ Python \ Codes \ check_ip.py ", wiersz 8 print '% s jest prawidłowym adresem IP% s. " % (ip, ip.version) ^ Błąd składni: nieprawidłowa składnia C: \ Python \ Kody> ' – Sabrina

5

muszę Markusowi Jarderotowi przydaj się na jego stanowisko - większość mojego postu jest zainspirowana jego.

Zauważyłem, że odpowiedź Markusa wciąż zawodzi w niektórych przykładach IPv6 w skrypcie Perla, do którego odnosi się jego odpowiedź.

Oto mój regex, która przechodzi wszystkie przykłady w tym Perl skryptu:

r"""^ 
    \s* # Leading whitespace 
    # Zero-width lookaheads to reject too many quartets 
    (?: 
     # 6 quartets, ending IPv4 address; no wildcards 
     (?:[0-9a-f]{1,4}(?::(?!:))){6} 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # 0-5 quartets, wildcard, ending IPv4 address 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,4}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # 0-4 quartets, wildcard, 0-1 quartets, ending IPv4 address 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,3}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:[0-9a-f]{1,4}(?::(?!:)))? 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # 0-3 quartets, wildcard, 0-2 quartets, ending IPv4 address 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,2}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:[0-9a-f]{1,4}(?::(?!:))){0,2} 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # 0-2 quartets, wildcard, 0-3 quartets, ending IPv4 address 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,1}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:[0-9a-f]{1,4}(?::(?!:))){0,3} 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # 0-1 quartets, wildcard, 0-4 quartets, ending IPv4 address 
     (?:[0-9a-f]{1,4}){0,1} 
     (?:::(?!:)) 
     (?:[0-9a-f]{1,4}(?::(?!:))){0,4} 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # wildcard, 0-5 quartets, ending IPv4 address 
     (?:::(?!:)) 
     (?:[0-9a-f]{1,4}(?::(?!:))){0,5} 
      (?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d) 
     (?:\.(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3} 
     | 
     # 8 quartets; no wildcards 
     (?:[0-9a-f]{1,4}(?::(?!:))){7}[0-9a-f]{1,4} 
     | 
     # 0-7 quartets, wildcard 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,6}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     | 
     # 0-6 quartets, wildcard, 0-1 quartets 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,5}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:[0-9a-f]{1,4})? 
     | 
     # 0-5 quartets, wildcard, 0-2 quartets 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,4}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,1}[0-9a-f]{1,4})? 
     | 
     # 0-4 quartets, wildcard, 0-3 quartets 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,3}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,2}[0-9a-f]{1,4})? 
     | 
     # 0-3 quartets, wildcard, 0-4 quartets 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,2}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,3}[0-9a-f]{1,4})? 
     | 
     # 0-2 quartets, wildcard, 0-5 quartets 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,1}[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,4}[0-9a-f]{1,4})? 
     | 
     # 0-1 quartets, wildcard, 0-6 quartets 
     (?:[0-9a-f]{1,4})? 
     (?:::(?!:)) 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,5}[0-9a-f]{1,4})? 
     | 
     # wildcard, 0-7 quartets 
     (?:::(?!:)) 
     (?:(?:[0-9a-f]{1,4}(?::(?!:))){0,6}[0-9a-f]{1,4})? 
    ) 
    (?:/(?:1(?:2[0-7]|[01]\d)|\d\d?))? # With an optional CIDR routing prefix (0-128) 
    \s* # Trailing whitespace 
    $""" 

ja również ułożyła skrypt Pythona do przetestowania wszystkich tych przykładach IPv6; to jest here on Pastebin, ponieważ była zbyt duża, aby ją opublikować.

można uruchomić skrypt z wynikiem testu i przykładowych argumentów w postaci „[wynik] = [przykład]”, tak jak:

python script.py Fail=::1.2.3.4: pass=::127.0.0.1 false=::: True=::1 

lub można po prostu uruchomić wszystkie testy, określając żadnych argumentów, więc jak:

python script.py 

W każdym razie, mam nadzieję, że to pomoże komuś innemu!

+1

+1 za wysiłek :) –

+3

Chociaż podziwiam również twój wysiłek, myślę, że w twoim konstruktorze jest ogromna wada konstrukcyjna: Jest za duża! Nigdy bym nie ufał regexowi tej wielkości, który nie był używany przez tysiące ludzi od lat. – erikbwork

+1

@ erikb85: Rzuć okiem na skrypt, który zamieściłem w Pastebin. Zawiera 1154 testów różnych formatów IPv6 i przekazuje ** każdy ** ** z nich. Jeśli uważasz, że potrzebujesz więcej testów, możesz zmodyfikować mój skrypt, dodać testy i opublikować wyniki. – blag

0

Musiałem tylko sparsować adresy IP v4.Moje rozwiązanie opiera się na strategii dreszcze następująco:

 
def getIP():
valid = False
while not valid :
octets = raw_input("Remote Machine IP Address:").strip().split(".")
try: valid=len(filter(lambda(item):0<=int(item)<256, octets)) == 4
except: valid = False
return ".".join(octets)
10

Mam nadzieję, że to proste i dość pythonic:

def is_valid_ip(ip): 
    m = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", ip) 
    return bool(m) and all(map(lambda n: 0 <= int(n) <= 255, m.groups())) 
+0

Replika tego samego wzoru 3 razy ... – warfares

+4

;-)^(\ d {1,3} \.) {3} \ d {1,3} $ – warfares

0

wymyśliłem ten noob prosta wersja

def ip_checkv4(ip): 
     parts=ip.split(".") 
     if len(parts)<4 or len(parts)>4: 
      return "invalid IP length should be 4 not greater or less than 4" 
     else: 
      while len(parts)== 4: 
       a=int(parts[0]) 
       b=int(parts[1]) 
       c=int(parts[2]) 
       d=int(parts[3]) 
       if a<= 0 or a == 127 : 
        return "invalid IP address" 
       elif d == 0: 
        return "host id should not be 0 or less than zero " 
       elif a>=255: 
        return "should not be 255 or greater than 255 or less than 0 A" 
       elif b>=255 or b<0: 
        return "should not be 255 or greater than 255 or less than 0 B" 
       elif c>=255 or c<0: 
        return "should not be 255 or greater than 255 or less than 0 C" 
       elif d>=255 or c<0: 
        return "should not be 255 or greater than 255 or less than 0 D" 
       else: 
        return "Valid IP address ", ip 

    p=raw_input("Enter IP address") 
    print ip_checkv4(p) 
2

Rozważmy adresowej IPv4 jako " ip ".

if re.match(r'^((\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])$', ip): 
    print "Valid IP" 
else: 
    print "Invalid IP" 
Powiązane problemy