使用正则表达式验证IPv4地址

206

我一直在尝试获得一个高效的IPv4验证正则表达式,但是运气不太好。有一段时间似乎我已经通过 (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?(\.|$)){4} 得到了正确结果,但它产生了一些奇怪的结果:

$ grep --version
grep (GNU grep) 2.7
$ grep -E '\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?(\.|$)){4}\b' <<< 192.168.1.1
192.168.1.1
$ grep -E '\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?(\.|$)){4}\b' <<< 192.168.1.255
192.168.1.255
$ grep -E '\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?(\.|$)){4}\b' <<< 192.168.255.255
$ grep -E '\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?(\.|$)){4}\b' <<< 192.168.1.2555
192.168.1.2555

我搜索过,看是否已经有人问过并得到了回答,但其他答案似乎仅展示如何确定由1-3个数字组成的4组数字,或者对我没有用。


22
别忘了 A、A.B 和 A.B.C 也是有效的 IP 地址格式,不只是 A.B.C.D。真的,请尝试用“ping 2130706433”和“ping 127.1”,可以让你发笑。 - dty
1
我的变量在线 https://regexr.com/39hqf - Enginer
47个回答

203

现时最佳方案 (43个字符)

^((25[0-5]|(2[0-4]|1\d|[1-9]|\d)\d)\.?\b){4}$

这个版本在不使用某些正则表达式语言中不支持的负向先行断言的情况下,又缩短了6个字符。

最新且最短但难读版本 (49个字符)

^((25[0-5]|(2[0-4]|1\d|[1-9]|\d)\d)(\.(?!$)|$)){4}$

[0-9]可以在两个地方被替换成\d,虽然这会降低代码可读性,但肯定会更短。

更新一点、再短一点、第二难读版本 (55个字符)

^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|\d)[0-9])(\.(?!$)|$)){4}$

这个版本查找了250-5的情况,之后聪明地将200-249 100-199 10-99的所有可能情况通过或运算连接在一起。请注意,|)部分并不是一个错误,而实际上是将最后一个0-9范围的情况与其他情况连接在一起。我也省略了?:非捕获组部分,因为我们实际上不关心捕获的项--如果我们没有首先得到完整匹配,它们也不会被捕获。

旧、更短但难读版本 (63个字符)

^(?:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?\d)(\.(?!$)|$)){4}$

较旧(可阅读)版本 (70个字符)

^(?:(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|\d)(\.(?!$)|$)){4}$

它使用负向先行断言(?!)来排除IP可能以.结尾的情况。

备选方案,使用一些新技术 (71个字符)

^((25[0-5]|(2[0-4]|1\d|[1-9]|\d)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|\d)\d)$

在不支持先行断言的正则表达式实现中很有用。

最老版本 (115个字符)

^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}
    (?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$

我认为这是最精确严格的正则表达式,它不接受像000.021.01.0.这样的内容,而其他答案似乎都会接受,并需要额外添加正则表达式来拒绝类似于那种情况 - 例如以 .结尾的以0 开头的数字和IP。


5
到目前为止,这是该帖子中唯一正确的答案。其他答案会忽略0.0.0.0这样的地址,或接受混合八进制/十进制表示法,例如033.033.33.033,甚至允许999.999.999.999。以下正则表达式比该答案短10个字符,您觉得如何:(([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]) - anneb
1
@tinmarino,我撤销了你的编辑,因为它允许像192.168.000.1这样无效的地址。任何想要编辑此答案的人,请先在此处发表评论以避免出现此类问题 - 我通常会很快回复。当然,我始终在寻找更短/更好的解决方案。 - Danail Gabenski
1
@DanailGabenski(和其他人)为了记忆,您将最后一个[01]?[0-9][0-9]?替换为1[0-9]{2}|[1-9]?[0-9],因为您不喜欢前导0。再次感谢!我会将您的解决方案保留在我的正则表达式大师行李中。 - Tinmarino
3
你可以通过将2[0-4]1和短情况的[0-9]分离出来来缩短它。^(?:(25[0-5]|(?:2[0-4]|[1-9]\d?)(\.(?!$)|$)){4})$ - Clayton Singh
1
哇,@DanailGabenski,我一年多后回来发现有一个更短的版本,它不使用前瞻!太棒了!令人印象深刻! - undefined
显示剩余37条评论

131

你已经有了一个可行的答案,但是如果你好奇你原本的方法有什么问题,那么答案就是你需要在或运算周围加上括号,否则(\.|$)只有在数字小于200时才会被使用。

'\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b'
    ^                                    ^

43
这似乎也证实了类似于“192.168.1.1.1”这样的东西。 - cwd
3
应该这样吗:\b((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:(?<!\.)\b|\.)){4}?即以单词边界而不是行尾结束。此外,我已标记非捕获组以避免产生不必要的子匹配。注意:这仍未考虑@dty的评论,因为我对那种形式的IP不熟悉,尽管他正确指出它似乎有效。 - JohnLBevan
4
“09.09.09.09”是否被认为是有效的IP地址?这个正则表达式可以匹配它。但是,ping命令会显示错误消息“ping: cannot resolve 09.09.09.09: Unknown host”。我认为将匹配范围缩小到点十进制表示法匹配可能更明智。这个条目讨论了IP地址中前导零的问题。 - Ruifeng Ma
1
将模式更改为^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(\.|$)){4}\b以解决单个数字的零填充问题。 - Bren
3
请继续阅读,@danail-gabenski在下面提供了另一个正则表达式,可以处理更多的边缘情况。 - Mathieu Rollet
显示剩余5条评论

116
^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

接受:

127.0.0.1
192.168.1.1
192.168.1.255
255.255.255.255
0.0.0.0
1.1.1.01        # This is an invalid IP address!

拒绝:

30.168.1.255.1
127.1
192.168.1.256
-1.2.3.4
1.1.1.1.
3...3

通过单元测试在线尝试:https://www.debuggex.com/r/-EDZOqxTxhiTncN6/1


"3...3" IP地址怎么样?使用这个正则表达式可以接受3...3。 - Ankur Loriya
8
1.1.1.01是一个有效的IPv4地址吗?谢谢。 答:1.1.1.01不是一个有效的IPv4地址。 - odieatla
1
这个正则表达式1.1.1.01被认为是有效的IPv4地址。在线单元测试https://www.debuggex.com/r/-EDZOqxTxhiTncN6/1 - Enginer
顺便说一下,^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$ 可以得到与 https://www.debuggex.com/r/mz_-0dEm3wseIKqK 相同的结果,与 @Mark Byers 的答案非常相似。 - Enginer
错误的正面 1.1.1.00 - JesseBoyd
显示剩余3条评论

16

IPv4地址(精确匹配) 可以匹配0.0.0.0到255.255.255.255的IP地址,但会捕获无效的地址,例如1.1.000.1 使用此正则表达式可以精确匹配IP地址。 每个4个数字被存储在一个捕获组中,因此您可以访问它们以进行进一步处理。

\b
(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.
(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.
(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.
(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
\b

取自JGsoft RegexBuddy库

编辑:这个(\.|$)部分看起来很奇怪


3
不错!我对它进行了更有效的修改,似乎可以工作:"\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$){4}\b -- 谢谢! - Matthieu Cartier
2
@MatthieuCartier,你高效的正则表达式模式对我没用。 - R__raki__
1
255.255.255.000 不是一个有效的 IP。 - Stéphane GRILLON

12

我认为阅读此帖的许多人将寻求更简单的正则表达式,即使它们匹配一些技术上无效的IP地址。(正如其他地方所指出的那样,正则表达式可能并不是正确验证IP地址的工具。)

如果您不想匹配行的开头/结尾,请删除^并在适当的情况下用\b替换$

基本正则表达式(BRE)(已在GNU grep、GNU sed和vim上测试):

/^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$/

扩展正则表达式(ERE):

/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/
或者:
/^([0-9]+(\.|$)){4}/

兼容Perl的正则表达式(PCRE)(在Perl 5.18上测试):

/^\d+\.\d+\.\d+\.\d+$/
或者:
/^(\d+(\.|$)){4}/

Ruby (测试于 Ruby 2.1):

尽管本应为 PCRE,但出于某种原因,Ruby 允许使用此正则表达式,而 Perl 5.18 不允许:

/^(\d+[\.$]){4}/

这些测试都在这里在线进行。


1
你的正则表达式 /^(\d+[\.$]){4}/ 是用于 5.5.5.5. 而不是 5.5.5.5 - Vadim

9

我在寻找类似的IPv4地址正则表达式 - 一个也能阻止常用私有IP地址的验证(192.168.x.y,10.x.y.z,172.16.x.y),所以使用了负向先行断言来实现这一点:

(?!(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.).*)
(?!255\.255\.255\.255)(25[0-5]|2[0-4]\d|[1]\d\d|[1-9]\d|[1-9])
(\.(25[0-5]|2[0-4]\d|[1]\d\d|[1-9]\d|\d)){3}

这些当然应该在一行上,为了可读性,格式化为3个单独的行

正则表达式可视化

Debuggex演示

虽然它可能没有进行速度优化,但在仅寻找“真实”互联网地址时效果良好。

以下内容将(并应该)失败:

0.1.2.3         (0.0.0.0/8 is reserved for some broadcasts)
10.1.2.3        (10.0.0.0/8 is considered private)
172.16.1.2      (172.16.0.0/12 is considered private)
172.31.1.2      (same as previous, but near the end of that range)
192.168.1.2     (192.168.0.0/16 is considered private)
255.255.255.255 (reserved broadcast is not an IP)
.2.3.4
1.2.3.
1.2.3.256
1.2.256.4
1.256.3.4
256.2.3.4
1.2.3.4.5
1..3.4

可用(而且应该使用)的IP地址:

1.0.1.0         (China)
8.8.8.8         (Google DNS in USA)
100.1.2.3       (USA)
172.15.1.2      (USA)
172.32.1.2      (USA)
192.167.1.2     (Italy)

如果有人正在寻找验证“不包括常见的私有 IP 地址”的互联网 IP 地址的方法,这里提供了一些参考。


演示似乎不再可用。 - Brad Turek

6
这里有一个更好的版本,附带通过/失败的IP。
/^((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$/

接受

127.0.0.1
192.168.1.1
192.168.1.255
255.255.255.255
10.1.1.1
0.0.0.0

拒绝

1.1.1.01
30.168.1.255.1
127.1
192.168.1.256
-1.2.3.4
1.1.1.1.
3...3
192.168.1.099

5

这段代码适用于我,非常简单。

在这里,我已经取得了IP地址的值,并尝试将其与正则表达式匹配。

ip="25.255.45.67"    

op=re.match('(\d+).(\d+).(\d+).(\d+)',ip)

if ((int(op.group(1))<=255) and (int(op.group(2))<=255) and int(op.group(3))<=255) and (int(op.group(4))<=255)):

print("valid ip")

else:

print("Not valid")

以上条件检查了所有4个八位字节的值是否超过了255,如果超过了则无效。但是在应用此条件之前,我们必须将它们转换为整数,因为该值为字符串。

group(0)打印匹配的输出,而group(1)打印第一个匹配的值,这里是"25"等等。


欢迎来到StackOverflow。如果您能详细说明为什么您的答案可以解决问题,那就太好了。仅包含代码的答案通常是不好的答案,因为它们无法帮助其他程序员理解他们做错了什么。 - Davide Vitali
请在您的代码中使用适当的缩进,使用户可以轻松阅读。 - Syed Mehtab Hassan

5
/^(?:(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(?1)$/m

演示


注意:这个在Perl中可行,但在Java中不行 :-( - ggrandes

5
上面的答案是正确的,但如果IP地址不在行末而是在文本中间,那该怎么办呢?这个正则表达式甚至可以处理这种情况。
代码:'\b((([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]))\b' 输入文本文件:
ip address 0.0.0.0 asfasf
 sad sa 255.255.255.255 cvjnzx
zxckjzbxk  999.999.999.999 jshbczxcbx
sjaasbfj 192.168.0.1 asdkjaksb
oyo 123241.24121.1234.3423 yo
yo 0000.0000.0000.0000 y
aw1a.21asd2.21ad.21d2
yo 254.254.254.254 y0
172.24.1.210 asfjas
200.200.200.200
000.000.000.000
007.08.09.210
010.10.30.110

输出文本:

0.0.0.0
255.255.255.255
192.168.0.1
254.254.254.254
172.24.1.210
200.200.200.200

1
这个被标记为负面,直到我给它投了一票。我已经试图做到这一点超过(比我想承认的)的时间了。它不会捕获一行中有多个点-四元组的情况,但对于我的用例,我可以接受这一点。这是一个很好的答案,需要更多的投票! - anastrophe

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