【问题标题】:How to validate IP address in Python? [duplicate]如何在 Python 中验证 IP 地址? [复制]
【发布时间】:2010-09-24 01:45:31
【问题描述】:

验证用户输入的 IP 是否有效的最佳方法是什么?它以字符串形式出现。

【问题讨论】:

  • 我只想指出,如果广播地址不被视为有效地址,那么到目前为止建议的每一个解决方案都会失败。您必须针对子网掩码进行测试以查看它是否是广播地址。
  • import ipaddress; ipaddress.ipaddress(your_input_text) 并抓住ValueError。它在标准库中。

标签: python validation networking ip-address


【解决方案1】:

不要解析它。随便问问。

import socket

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

【讨论】:

  • 嗯,似乎接受诸如“4”和“192.168”之类的东西,并用零默默地填充其余部分。我敢肯定,在技术上是有效的,但并不完全符合我的预期。
  • 不,不适用于所有合法 IP 地址:>>> socket.inet_aton("2001:660::1") Traceback(最近一次调用最后):文件“”,第 1 行,在 socket.error: 传递给 inet_aton 的非法 IP 地址字符串
  • @bortzmeyer: socket.inet_pton(socket_family, address) 如果你想支持 ip6 就是你想要的。您仍然需要指定族。 inet_aton 明确地不支持除了 ip4 之外的任何东西。
  • 看起来 Richo 的回答解决了 krupan 和 bortzmeyer 的问题。使用 socket.inet_pton 和 socket.AF_INET 或 socket.AF_INET6 作为族来验证 ipv4 和 ipv6,而不接受不完整的地址。
  • inet_aton() 在接受“4”、“192.168”和“127.1”时不接受“无效”IP,它只是使用底层 C 行为 - 请参阅文档。 127.1 将 127 放在顶部八位字节中,并将 1 解析为一个 24 位数字,并将其拆分为其余三个八位字节。这个想法是支持增加 IP 的 /16 范围,因此您可以转到 172.16.1...172.16.255,然后是 172.16.256,而不是更改您的数学以转到 172.16.1.0。
【解决方案2】:

从 Python 3.4 开始,检查 IPv6 或 IPv4 地址是否正确的最佳方法是使用 Python 标准库模块 ipaddress - IPv4/IPv6 操作库 s.a. https://docs.python.org/3/library/ipaddress.html 获取完整文档。

示例:

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

对于其他版本:Github、phihag / Philipp Hagemeister,“旧 Python 版本的 Python 3.3 的 ipaddress”,https://github.com/phihag/ipaddress

来自 phihag 的反向移植可用,例如在 Anaconda Python 2.7 中 & 包含在安装程序中。公司https://docs.continuum.io/anaconda/pkg-docs

使用 pip 安装:

pip install ipaddress

s.a.: ipaddress 1.0.17, "IPv4/IPv6 操作库", "3.3+ ipaddress 模块的端口", https://pypi.python.org/pypi/ipaddress/1.0.17

【讨论】:

  • 我收到此错误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>
  • 谢谢@Yohann。对于 Python 3.5,打印时需要括号。否则,代码将产生错误。由于这个空间有限,我将在下面的答案部分更新代码。希望这对其他人也有帮助。
  • 这将返回对test.example.com 的错误响应。我得到了IPv6Address(u'7465:7374:2e65:7861:6d70:6c65:2e63:6f6d')
  • 对不起,这里是新手。 sys.argv[0]/[1] 在这个例子中扮演什么角色?这些只是您在触发脚本时传递的测试参数(示例 IP 地址)还是在实施时需要它们?从阅读here 来看,它们似乎只是测试参数。
【解决方案3】:
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

【讨论】:

  • 为什么会有一行:"return address.count('.') == 3" ??这是你调试时遗留下来的吗?
  • @quux:没有。这是一个漫长的讨论,人们不喜欢至少在 Linux 和 Windows 上缩短地址被认为是可以接受的事实。例如,socket.inet_aton('127.1') 的计算结果为 '\x7f\x00\x00\x01'(即与 '127.0.0.1' 完全一样)。我在 SO 的其他地方进行了这个令人厌烦且冗长的讨论,但我不记得在哪里了。
  • 在 windows 上怎么样?
  • 请注意,这是一个仅适用于 unix 的答案
  • @cowlinator inet_pton 仅存在于 Unix 中,而 inet_aton 存在于所有平台中,因此这是一个“以 unix 为主”的答案。
【解决方案4】:

IPy module(设计用于处理 IP 地址的模块)将针对无效地址抛出 ValueError 异常。

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

但是,就像达斯汀的回答一样,它会接受诸如“4”和“192.168”之类的内容,因为如上所述,它们是 IP 地址的有效表示。

如果您使用的是 Python 3.3 或更高版本,它现在包含 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

对于 Python 2,如果安装 python-ipaddress,则可以使用 ipaddress 获得相同的功能:

pip install ipaddress

此模块与 Python 2 兼容,并提供与 Python 3.3 以来 Python 标准库中包含的 ipaddress 模块非常相似的 API。更多详情here。在 Python 2 中,您需要将 IP 地址字符串显式转换为 unicode:ipaddress.ip_address(u'127.0.0.1')

【讨论】:

  • 好主意。迄今为止提出的唯一一种适用于所有 IP 地址的解决方案。 >>> 从 IPy 导入 IP >>> IP("2001:660::1") IP('2001:660::1')
  • 对于 python 2,pip install ipaddress 你得到几乎相同的 API :)
  • 关于import ipaddress,当我传递一个IPv4地址时,我得到像IPv4Address('127.0.0.1')这样的输出。但是当我尝试将其转换为string 以检查它是否包含IPv4IPv6 时,我刚刚获得了IP。我如何在代码中知道它是IPv4 还是IPv6if "IPv4" in str(type(val)): 是个好主意吗?
  • 使用完全相同的步骤得到ipaddress.AddressValueError: '127.0.0.1' does not appear to be a n IPv4 or IPv6 address. Did you pass in a bytes (str in Python 2) instead of a unicode object?。错误消息中提供的帮助帮助我解决了这个问题。在 Python 2 中你需要 u'...'
  • 解决方法就这么简单:try: return bool(ipaddress.ip_address(ip)) except ValueError: return False
【解决方案5】:
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

IPv6 版本使用“(?:(?&lt;=::)|(?&lt;!::):)”,在支持环视条件的正则表达式引擎上,可以用“(?(?&lt;!::):)”替换。 (即 PCRE、.NET)

编辑:

  • 删除了原生变体。
  • 扩展了正则表达式以符合 RFC。
  • 为 IPv6 地址添加了另一个正则表达式。

编辑2:

我找到了一些讨论如何使用正则表达式解析 IPv6 地址的链接:

编辑3:

终于写出了一个通过所有测试的模式,我也很满意。

【讨论】:

  • 否,仅适用于 IPv4 地址。
  • test-ipv6-regex.pl 是黄金+1
  • 似乎不适用于使用方括号的 IPv6 地址(例如指定端口号):[2001:4860:4860::8888]:80
  • 我认为这种方式非常非pythonic并且过于复杂。使用来自 ipaddress.ip_address 的简单调用就足够了。
【解决方案6】:

我希望它足够简单和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()))

【讨论】:

  • 你重复相同的模式 3 次...
  • ;-) ^(\d{1,3}\.){3}\d{1,3}$
  • @warfares 是有原因的,它需要将组分开以检查值是否在 0 和 255 范围内,这就是返回的第二部分所做的,如果你想仅使用您的正则表达式使用 return bool(m)。
【解决方案7】:

我觉得这样就可以了……

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

【讨论】:

  • 如果用户键入“a.b.c.d”而不是整数,您可能希望从 int() 捕获 ValueError 异常。
  • 错误代码,仅适用于 IPv4 地址。
  • 这里 Python 的 int() 强制转换过于宽松;例如,它会去除空格。
  • 192.168.178.0030 将是有效的。
  • 不适用于 ipv6。
【解决方案8】:

将 IPv4 地址视为“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"

【讨论】:

    【解决方案9】:

    我必须非常感谢 Markus Jarderot 的帖子 - 我的大部分帖子都受到他的启发。

    我发现 Markus 的答案仍然无法通过他的答案引用的 Perl 脚本中的一些 IPv6 示例。

    这是我的正则表达式,它传递了该 Perl 脚本中的所有示例:

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

    我还编写了一个 Python 脚本来测试所有这些 IPv6 示例;是here on Pastebin,因为它太大,无法在此处发布。

    您可以以“[result]=[example]”的形式运行带有测试结果和示例参数的脚本,例如:

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

    或者你可以简单地通过不指定参数来运行所有的测试,比如:

    python script.py
    

    无论如何,我希望这对其他人有帮助!

    【讨论】:

    • 虽然我也很佩服你的努力,但我认为你的构造存在一个巨大的设计缺陷:它太大了!我永远不会相信这种大小的正则表达式,它多年来没有被成千上万的人使用过。
    • @erikb85:看看我发布到 Pastebin 的脚本。它包含 1154 项不同 IPv6 格式的测试,并且通过了每一项。如果您认为需要进行更多测试,请随时修改我的脚本、添加测试并发布结果。
    【解决方案10】:

    我想出了这个简单的版本

    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)
    

    【讨论】:

      【解决方案11】:

      我只需要解析 IP v4 地址。我基于 Chills 策略的解决方案如下:

      def getIP():
      有效 = 假
      虽然无效:
      octets = raw_input("远程机器 IP 地址:").strip().split(".")
      尝试:有效=len(过滤器(lambda(项目):0 除外:有效 = 假
      返回 ".".join( 八位字节 )

      【讨论】:

        猜你喜欢
        • 2013-12-06
        • 2011-10-05
        • 2019-11-14
        • 1970-01-01
        • 2011-04-11
        • 2013-07-08
        • 1970-01-01
        • 2013-04-16
        相关资源
        最近更新 更多