Perl正则表达式匹配IP地址

12

我写了这段代码,但它不起作用。 有人能指出问题吗?

sub match_ip()
{
  my $ip = "The IP address is 216.108.225.236:60099";
  if($ip =~ /(\d{1-3}\.\d{1-3}\.\d{1-3}\.\d{1-3}\:\d{1-5})/)
  {
      print "$1\n";
  }
}

编辑: 我只想提取IP地址,不进行任何验证。


8
不要自己编写正则表达式,你可以考虑使用一个经过充分文档化和测试的模块,例如Regexp::Common - dgw
3
这里有不止几个问题。使用其中一个模块,或查看所有模块所做的事情以正确地完成它 :) - brian d foy
1
你是想验证还是仅提取IP地址?因为它在前者方面的工作非常糟糕。 - ikegami
我只想提取,不验证。 - cppcoder
2
你也不想提取那些不能成为IP地址的东西,因此进行验证非常容易,有助于减少误报。 - brian d foy
事实上,仅仅为了验证,我不想让我的脚本依赖于任何模块。我期望在我处理的文件中只有有效的IP地址。 - cppcoder
12个回答

15

{1-3}更改为{1,3},同样的操作也适用于{1-5} -> {1,5}


2
@briandfoy - 这个答案对您也可以吗?正如我之前提到的,有时候 OP 想要解决问题的简单解决方案,而不是复杂的代码。在这种情况下,不需要进行 IP 验证,因为可能 OP 需要解析包含有效 IP 地址的输出。因此,这个答案正是 OP 所寻找的。感谢 M42 提供满足 OP 需求的简单答案。 - Ωmega
@stackoverflow:谢谢。前三个答案都是根据 OP 正确的。这就是为什么我投了两票而不是我的。 - Toto
1
@M42 - 我在底部的帖子(回答)因#briandFoy及其追随者毫无理由地被点踩,所以我试图评论您的答案以向他展示这些答案是好的。您的答案是最早的,因此您应该得到赞同和接受。 - Ωmega
这解决了正则表达式的问题,但没有解决任务问题。我认为这不符合需求,只是想要的东西。这两者经常相互矛盾。 - brian d foy
1
@briandfoy:你说得没错,但这正是 OP 想要的,解决了正则表达式的问题。 - Toto
1
@M42 - 没错,M42。正如OP所要求的那样,它已经通过正则表达式解决了。不幸的是,#briandfoy经常试图将简单的任务变成复杂的问题,而这在大多数情况下并不是OP想要的。 - Ωmega

7
在TIMTOWTDI的精神下,这里有另一种方法: Regexp::Common 中的 Regexp::Common::net 可能会包含您所需的正则表达式。请注意保留HTML标记。

在所有答案中,这个最为准确,因为它提供了一个可用的解决方案,适用于IPv4和IPv6。尽管OP似乎只关注IPv4。肯定还有其他人会对IPv6正则表达式感兴趣。 - Jacques
你能否更新你的答案,包括更多关于如何在脚本中使用它的解释?现在它只是一个链接。 - slhck

4

或者,您可以使用Data::Validate::IP,但需要注意的是它无法识别端口,因此您需要在:上进行split

use strict;
use warnings;
use Data::Validate::IP;

my $ip_with_port="216.108.225.236:60099";
my $ip=(split /:/,$ip_with_port)[0];

my $validator=Data::Validate::IP->new;

if($validator->is_ipv4($ip))
{
  print "Yep, $ip is a valid IPv4 address.\n";
}
else
{
  print "Nope, $ip is not a valid IPv4 address.\n";
}

输出结果为:
Yep, 216.108.225.236 is a valid IPv4 address.

4
OP的问题是要找到匹配的正则表达式,而不是验证IP地址。你的脚本使用IP地址作为输入,但OP的输入是一个可能包含IP地址但不仅限于IP地址的字符串。 - Ωmega
1
@stackoverflow - TIMTOWTDI。此外,a)OP试图组合一个正则表达式以便验证IP地址,b)如果您查看Data :: Validate :: IP的源代码,它使用正则表达式。因此,无论您喜不喜欢,我的答案都是相关的。 - user554546
1
为什么用这么强烈的措辞?我喜欢你的回答,但我评论你的回答只有一个原因——我在 OP 的问题中没有看到他正在寻找 IP 验证的任何注释……即使现在,在你的评论中说 OP 正在要求这个,我也没有看到它。无论如何……你的代码是正确的 IP 验证解决方案。就这样。只是不确定那是否是 OP 想要的。也许他会回来给我们澄清他的需求…… - Ωmega

2

请用逗号替换破折号。

/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5})/

2
虽然在CPAN上有已记录并经过测试的模块可以匹配和验证IP地址,但你肯定有充分的理由不使用它们。就个人而言,我从来没有真正需要使用它们进行验证,因为我信任并正确输入了数据。
以下是正则表达式的简化版本,但它也有其自身的缺陷:
while (my $ip = <DATA>)  {
    chomp $ip;
    # older version
    # if($ip =~ /(\d{1-3}\.\d{1-3}\.\d{1-3}\.\d{1-3}\:\d{1-5})/)

    # see below for explanation
    if ($ip =~ /\b(\d{1,3}(?:\.\d{1,3}){3}:\d{1,5})\b/)
    {
        print "$ip - matches\n";
    } else {
        print "$ip - does not match\n";
    }
}

__DATA__
216.108.225.236:60099
4.2.2.1:1
216.108.225.236:0
1216.1108.1225.1236:1234
216.108.225.236x:123
9216.108.225.236:8472
10.10.10.10

结果:

216.108.225.236:60099 - matches
4.2.2.1:1 - matches
216.108.225.236:0 - matches
1216.1108.1225.1236:1234 - does not match
216.108.225.236x:123 - does not match
9216.108.225.236:8472 - does not match
10.10.10.10 - does not match

说明:
Explanation:
/\b             # word boundary
(               # start memory capture group 1
\d{1,3}         # one to three digits, first octat
(:?             # start non memory capture group, notice ?:
  \.\d{1,3}     # a literal dot followed by an ip octet
)               # end non memory capture group
{3}             # three times of dots and ip octets
:               # match a colon
\d{1,5}         # port number, one to five digits
)               # end of memory capture group 1
\b              # word boundary

希望这能有所帮助。

1
如果你要做这么多的工作,最好让正则表达式匹配正确范围的数字。你应该测试一些像127.1这样的东西,这是一个有效的地址,当我做错这种事情时曾经困扰过我。 - brian d foy
您的测试数据不完整。像 333.108.225.236:123 这样的数据是无效的 IP,但会通过您的检查。 - Will Sheppard
1
@Will, Brian:你们可能忽略了Ashish描述中的“since I trusted/feeded the input”这一部分。有时使用外部模块是不可能、不受欢迎、或者不被期望的,这种情况下需要提及“手动”解决方案。 - taiko

1

一个较短版本的:

/((\d{1,3}\.){3}\d{1,3}\:\d{1,5})/

这个表达式与OP的相同,只有两个变化:
  1. 破折号“-”变成逗号“,”,即 {1,3}{1,5},如其他答案已经指出的那样。

  2. 我的改变是将 \d{1,3}\.\d{1,3}\.\d{1,3}\. 组合成一个,即 (\d{1,3}\.) 并使用 {3} 精确匹配 3 次。

感谢 @starball 的指导 ;)

给出一个简要说明它如何工作/解决问题的方法以及它与现有答案的不同之处会很好。 - starball

1
#!/usr/bin/perl

$str = 'IP address is : 70.21.311.105';

    if ($str =~ m/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/) {
        if ($1 <= 255 && $2 <= 255 && $3 <= 255 && $4 <= 255 ) {
            print "Valid $str\n";
    } else {
          print "invalid IP $str\n";
    }
}


__END__

1
不需要第二个 if 条件,可以与第一个条件一起写成这样 if($str =~ m/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/ && ($1<=255 && $2<=255 && $3<=255 && $4<=255 )) - serenesat

1
这可能有所帮助:

my $ip = "195.249.61.14";

my @ips = (
    "set protocols bgp group IBGP-RRCL-CUSTOMER neighbor 195.249.61.142",
    "set protocols bgp group IBGP-RRCL-CUSTOMER neighbor 195.249.61.14",
    "set protocols bgp group IBGP-RRCL-CUSTOMER neighbor 195.249.61.141"
);

foreach (@ips) {
   print "$_\n" if ( /\b$ip\b/ );
}

输出:

set protocols bgp group IBGP-RRCL-CUSTOMER neighbor 195.249.61.14

0

试一下:

$variablename=~m/((((0-9)|((1-9)(0-9))|(1([0-9]){2})|(2[0-4][0-9])|(2[5][0-5]))\.){3})((0-9)|((1-9)(0-9))|(1([0-9]){2})|(2[0-4][0-9])|(25[0-5]))/)

0
use strict;
use warnings;
open(FH,"<fileName.txt") or die "file not found ,$_";
while(my $line=<FH>)
{
push(my @arr,($line));
foreach my $arrVal (@arr)
{           
if($arrVal=~/IPv4 Address(?=.*\b((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2  [0-4]\d|[0-1]?\d?\d)){3})\b)/)
{
print "$arrVal\n";
}
}

尽管这段代码可能回答了问题,但提供关于它为什么和/或如何回答问题的额外上下文会显著提高其长期价值。请[编辑]您的答案以添加一些解释。 - Toby Speight

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