在Python中如何检查一个字符串是否符合IP地址模式?

60
什么是检查字符串是否符合特定模式的最快方法?正则表达式是最好的方法吗?
例如,我有一堆字符串,并想检查每个字符串是否为有效的IP地址(在这种情况下,有效意味着正确的格式),使用正则表达式是最快的方法吗?还是有类似字符串格式化的更快的方法。 目前,我已经在做这样的事情:
for st in strs:
    if re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', st) != None:
       print 'IP!'
21个回答

105

更新

下面的原始答案适用于2011年,但自2012年以来,最好使用Python的ipaddress标准库模块 - 除了检查IPv4和IPv6的IP有效性外,它还可以做很多其他事情。


看起来你正在尝试验证IP地址。正则表达式可能不是最好的工具。
如果你想接受所有有效的IP地址(包括一些你可能甚至不知道是有效的地址),那么你可以使用IPy (来源)
from IPy import IP
IP('127.0.0.1')

如果IP地址无效,它将抛出一个异常。
或者你可以使用socket (源代码)
import socket
try:
    socket.inet_aton(addr)
    # legal
except socket.error:
    # Not legal

如果你真的只想匹配由4个十进制部分组成的IPv4地址,那么你可以按照点号进行分割,并检查每个部分是否是介于0到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

请注意,您的正则表达式没有进行这个额外的检查。它会将999.999.999.999作为一个有效的地址接受。

接受这个IPy。我最终使用IPy部分原因是由于@Alex的IPv6观点。 - Tommy Morene
1
值得注意的是,套接字模块存在安全问题,它利用了glibc inet_aton()函数,该函数“由于历史原因接受尾随垃圾”,如此报道:https://bugzilla.redhat.com/show_bug.cgi?id=1347549。红帽产品安全将此问题评为中等安全影响,并且不太可能在短时间内得到解决。鉴于此,我认为一个好的正则表达式确实是处理这个问题的最佳工具。 - aitch-hat
(我编辑了答案,指向Python的ipaddress - 我为干涉文本而道歉,但似乎很多互联网都指向这个答案 - 我认为这里的链接将帮助更多的人,而不是7年后提出的晦涩答案,甚至第二个答案可能会被忽视) - jsbueno
“some addresses you probably didn't even know were valid” 是什么意思? - Sam
inet_aton() 无法验证不合法的字符串,例如 "1.2"、"1.2.3"。 - Prajwal
显示剩余2条评论

57

如果您使用Python3,则可以使用ipaddress模块,相关文档可参考http://docs.python.org/py3k/library/ipaddress.html。例如:

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

16

我认为在Python 3.6中更简单,因为ipaddress模块已经包含在内:

import ipaddress

    def is_ipv4(string):
        try:
            ipaddress.IPv4Network(string)
            return True
        except ValueError:
            return False

2
我认为最好使用“except ipaddress.AddressValueError:”来捕获实际错误。 - Javier Ruiz
@JavierRuiz 对我来说,Python 3.8引发了ValueError而不是ipaddress.AddressValueError - run_the_race
@run_the_race 对我来说它返回了 AddressValueError。例如,尝试使用 ipaddress.IPv4Network("123.3456.234.34")。 AddressValueError: 在 '123.3456.234.34' 中的 '3456' 最多允许3个字符。 - Javier Ruiz
它实际上可以引发两种异常。这取决于输入。 - stackprotector

14

通常我是为数不多的Python专家之一,坚定地捍卫正则表达式(它们在Python社区中声名狼藉),但这并不是其中之一——接受(例如)'333.444.555.666'作为"IP地址"是非常糟糕的,而且如果您需要在匹配RE后进行更多检查,使用RE的大部分意义也将丧失。因此,我全心全意地支持@Mark的建议:IPy具有普适性和优雅性(包括对IPv6的支持!),如果只需要IPv4,则可以使用字符串操作和int检查(但是,请三思而后行,再三思考一下——IPv6的时间已经到来了!):

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

我宁愿这样做,而不是编写一个复杂的正则表达式来匹配0到255之间的数字!-)


使用 a<=x<b 等更简洁的方式,比我的尝试更加优雅。 - Mark Byers
1
尽管我完全同意你回答的主要观点,但是这里发布的代码仅检查长度为4,而像127.1这样的地址是有效的(socket.inet_aton也同意,并且可以ping通这些地址)。这实际上加强了使用IPy或socket模块的必要性。 - 0xc0de

6

不需要重新验证的另一种验证方法:

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)

1
在诉诸于 re 之前,应该尝试使用这样的方法。 - Dave
如果抛出异常,最好默认返回false。例如:'192.168.1.abc' - FelixHo

3

本页面上其他正则表达式答案可能接受一个数字超过255的IP地址。

这个正则表达式将避免这个问题:

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

3
您的正则表达式没有检查字符串的结尾,因此它会匹配以下内容:
123.45.67.89abc123boogabooga

为了解决这个问题,请使用以下方法:
'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'

请注意结尾的$

最后,在Python中通常使用is not None代替!= None


2
安装 netaddr 包
sudo pip install netaddr

然后你可以这样做

>>> from netaddr import valid_ipv4
>>> valid_ipv4('11.1.1.2') 
True
>>> valid_ipv4('11.1.1.a')
False

同时您可以从该字符串创建IPAddress对象,并进行更多与IP相关的操作。

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

2

可以使用 iptools 工具。

import iptools
ipv4 = '1.1.1.1'
ipv6 = '5000::1'
iptools.ipv4.validate_ip(ipv4) #returns bool
iptools.ipv6.validate_ip(ipv6) #returns bool

简单明了,适合我。 - FractalSpace

2
如果您要验证IP地址,我建议采用以下方法:
import socket

try:
    socket.inet_aton(addr)
    return True
except socket.error:
    return False

如果您只是想检查它是否以正确的格式存在,那么您需要对所有 法律基础 进行检查(不仅仅是第10个编号的基础)。
另外,如果这些 IP 地址仅限于 IPv4(没有IPv6),那么您可以查找有效地址并使用split()(获取 IP 的单独 组件)和int()(进行类型强制转换以进行比较)。有关有效 IPv4 规则的快速参考请点击此处

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接