186 votes

Comment valider une adresse IP en Python ?

Quelle est la meilleure façon de valider qu'une IP entrée par l'utilisateur est valide ? Elle est entrée sous forme de chaîne.

0 votes

Je tiens simplement à souligner que si une adresse de diffusion n'est pas considérée comme une adresse valide, alors toutes les solutions proposées jusqu'à présent échouent. Vous devez effectuer un test par rapport à un masque de sous-réseau pour voir s'il s'agit d'une adresse de diffusion.

0 votes

import ipaddress; ipaddress.ipaddress(your_input_text) et attraper le ValueError . C'est dans la stdlib.

193voto

Dustin Points 35205

Ne l'analyse pas. Demandez simplement.

import socket

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

24 votes

Hmm, semble accepter des choses comme "4" et "192.168" et remplit silencieusement le reste avec des zéros. Techniquement valide, j'en suis sûr, mais pas tout à fait ce que j'attendais.

1 votes

Ce sont des représentations valides des adresses IP. 127.1 est localhost, 1172703390 est mon serveur web. Si vous voulez vous assurer que c'est en quadrature du cercle, vous pouvez aussi vérifier que len(addr.split('.')) == 4

1 votes

@krupan : vous pourriez combiner ce qui précède avec un test pour "len(addr.split(".")) == 4" si vous souhaitez rejeter les adresses plus courtes.

108voto

Yohann Points 1224

À partir de Python 3.4, la meilleure façon de vérifier si une adresse IPv6 ou IPv4 est correcte est d'utiliser le module de la bibliothèque standard Python ipaddress - Bibliothèque de manipulation IPv4/IPv6 s.a. https://docs.python.org/3/library/ipaddress.html pour une documentation complète.

Exemple :

#!/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])

Pour d'autres versions : Github, phihag / Philipp Hagemeister, "Python 3.3's ipaddress for older Python versions", https://github.com/phihag/ipaddress

Le backport de phihag est disponible par exemple dans Anaconda Python 2.7 et est inclus dans Installer. s.a. https://docs.continuum.io/anaconda/pkg-docs

Pour installer avec pip :

pip install ipaddress

s.a. : ipaddress 1.0.17, "Bibliothèque de manipulation IPv4/IPv6", "Port du module ipaddress 3.3+", https://pypi.python.org/pypi/ipaddress/1.0.17

0 votes

J'obtiens cette erreur C:\Python\Codes>check_ip.py File "C:\Python\Codes\check_ip.py", line 8 print '%s is a correct IP%s address.' % (ip, ip.version) ^ SyntaxError: invalid syntax C:\Python\Codes>

2 votes

Merci @Yohann. Pour Python 3.5, les parenthèses sont nécessaires à l'impression. Sinon, le code produira une erreur. Comme cet espace est limité, je vais mettre à jour le code dans la section réponse ci-dessous. J'espère que cela aidera les autres aussi.

0 votes

Cela renverra une réponse incorrecte pour test.example.com . J'ai eu IPv6Address(u'7465:7374:2e65:7861:6d70:6c65:2e63:6f6d')

81voto

tzot Points 32224
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

1 votes

Pourquoi la ligne : "return address.count('.') == 3" ? ?? Est-ce un reste de votre débogage ?

21 votes

@quux : non. C'est une longue discussion, et les gens n'aiment pas le fait que, au moins sur Linux et Windows, les adresses raccourcies sont considérées comme acceptables. Par exemple, socket.inet_aton('127.1') évalue à '\x7f\x00\x00\x01' (c'est-à-dire exactement comme le fait '127.0.0.1'). J'ai déjà eu cette discussion longue et fastidieuse ailleurs sur SO, mais je n'arrive pas à me souvenir où.

2 votes

Et sous Windows ?

69voto

Samat Jain Points 3859

Le site Module IPy (un module conçu pour traiter les adresses IP) lancera une exception ValueError pour les adresses invalides.

>>> 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'

Cependant, comme la réponse de Dustin, il acceptera des choses comme "4" et "192.168" puisque, comme mentionné, ce sont des représentations valides des adresses IP.

Si vous utilisez Python 3.3 ou une version ultérieure, il inclut désormais la fonction module d'adresse IP :

>>> 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

Pour Python 2, vous pouvez obtenir la même fonctionnalité en utilisant ipaddress si vous installez python-ipaddress :

pip install ipaddress

Ce module est compatible avec Python 2 et fournit une API très similaire à celle du module ipaddress inclus dans la bibliothèque standard de Python depuis Python 3.3. Plus de détails aquí . En Python 2, vous devrez convertir explicitement la chaîne d'adresse IP en unicode : ipaddress.ip_address(u'127.0.0.1') .

0 votes

Excellente idée. La seule solution présentée jusqu'à présent qui fonctionne avec toutes les adresses IP. >>> from IPy import IP >>> IP("2001:660::1") IP('2001:660::1')

0 votes

Pour python 2, pip install ipaddress et vous obtenez presque la même API :)

0 votes

Concernant import ipaddress quand j'ai passé une adresse IPv4, j'ai obtenu un résultat comme suit IPv4Address('127.0.0.1') . Mais quand j'ai essayé de le convertir en string pour vérifier s'il contient IPv4 o IPv6 Je viens d'avoir l'IP. Comment puis-je savoir en code si c'est une IPv4 o IPv6 ? Est-ce que if "IPv4" in str(type(val)): une bonne idée ?

46voto

Markus Jarderot Points 33893
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

La version IPv6 utilise " (?:(?<=::)|(?<!::):) ", qui pourrait être remplacé par " (?(?<!::):) "sur les moteurs regex qui supportent les conditionnels avec des solutions de contournement. (i.e. PCRE, .NET)

Editar:

  • J'ai laissé tomber la variante native.
  • Extension de la regex pour se conformer à la RFC.
  • Ajout d'une autre regex pour les adresses IPv6.

Edit2 :

J'ai trouvé des liens qui expliquent comment analyser les adresses IPv6 avec des expressions rationnelles :

Edit3 :

J'ai finalement réussi à écrire un modèle qui passe tous les tests, et dont je suis également satisfait.

1 votes

Non, cela ne fonctionne qu'avec les adresses IPv4.

0 votes

Test-ipv6-regex.pl est or +1

0 votes

Cela ne semble pas fonctionner avec les adresses IPv6 qui utilisent des crochets (par exemple pour spécifier un numéro de port) : [2001:4860:4860::8888]:80

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X