关于正则表达式:匹配有效IPv6地址的正则表达式

关于正则表达式:匹配有效IPv6地址的正则表达式

Regular expression that matches valid IPv6 addresses

我在编写匹配有效IPv6地址的正则表达式时遇到了麻烦,包括压缩格式的地址(每个字节对均省略了::或前导零)。

有人可以建议一个满足要求的正则表达式吗?

我正在考虑扩展每个字节对,并将结果与一个更简单的正则表达式匹配。


我无法获得@Factor Mystic的答案才能使用POSIX正则表达式,因此我编写了一个可用于POSIX正则表达式和PERL正则表达式的答案。

它应该匹配:

  • IPv6地址
  • 零压缩IPv6地址(rfc5952的2.2节)
  • 具有区域索引的链接本地IPv6地址(rfc4007的第11节)
  • IPv4嵌入式IPv6地址(rfc6052的第2部分)
  • IPv4映射的IPv6地址(rfc2765的2.1节)
  • IPv4转换的地址(rfc2765的2.1节)

IPv6正则表达式:

1
(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))

为了便于阅读,以下是上述正则表达式,将其在主要OR点处分为几行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# IPv6 RegEx
(
([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|          # 1:2:3:4:5:6:7:8
([0-9a-fA-F]{1,4}:){1,7}:|                         # 1::                              1:2:3:4:5:6:7::
([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|         # 1::8             1:2:3:4:5:6::8  1:2:3:4:5:6::8
([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|  # 1::7:8           1:2:3:4:5::7:8  1:2:3:4:5::8
([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|  # 1::6:7:8         1:2:3:4::6:7:8  1:2:3:4::8
([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|  # 1::5:6:7:8       1:2:3::5:6:7:8  1:2:3::8
([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|  # 1::4:5:6:7:8     1:2::4:5:6:7:8  1:2::8
[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|       # 1::3:4:5:6:7:8   1::3:4:5:6:7:8  1::8  
:((:[0-9a-fA-F]{1,4}){1,7}|:)|                     # ::2:3:4:5:6:7:8  ::2:3:4:5:6:7:8 ::8       ::    
fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|     # fe80::7:8%eth0   fe80::7:8%1     (link-local IPv6 addresses with zone index)
::(ffff(:0{1,4}){0,1}:){0,1}
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|          # ::255.255.255.255   ::ffff:255.255.255.255  ::ffff:0:255.255.255.255  (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
([0-9a-fA-F]{1,4}:){1,4}:
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])           # 2001:db8:3:4::192.0.2.33  64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address)
)

# IPv4 RegEx
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])

为了使上面的内容更容易理解,下面的"伪"代码复制了上面的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
IPV4SEG  = (25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])
IPV4ADDR = (IPV4SEG\.){3,3}IPV4SEG
IPV6SEG  = [0-9a-fA-F]{1,4}
IPV6ADDR = (
           (IPV6SEG:){7,7}IPV6SEG|                # 1:2:3:4:5:6:7:8
           (IPV6SEG:){1,7}:|                      # 1::                                 1:2:3:4:5:6:7::
           (IPV6SEG:){1,6}:IPV6SEG|               # 1::8               1:2:3:4:5:6::8   1:2:3:4:5:6::8
           (IPV6SEG:){1,5}(:IPV6SEG){1,2}|        # 1::7:8             1:2:3:4:5::7:8   1:2:3:4:5::8
           (IPV6SEG:){1,4}(:IPV6SEG){1,3}|        # 1::6:7:8           1:2:3:4::6:7:8   1:2:3:4::8
           (IPV6SEG:){1,3}(:IPV6SEG){1,4}|        # 1::5:6:7:8         1:2:3::5:6:7:8   1:2:3::8
           (IPV6SEG:){1,2}(:IPV6SEG){1,5}|        # 1::4:5:6:7:8       1:2::4:5:6:7:8   1:2::8
           IPV6SEG:((:IPV6SEG){1,6})|             # 1::3:4:5:6:7:8     1::3:4:5:6:7:8   1::8
           :((:IPV6SEG){1,7}|:)|                  # ::2:3:4:5:6:7:8    ::2:3:4:5:6:7:8  ::8       ::      
           fe80:(:IPV6SEG){0,4}%[0-9a-zA-Z]{1,}|  # fe80::7:8%eth0     fe80::7:8%1  (link-local IPv6 addresses with zone index)
           ::(ffff(:0{1,4}){0,1}:){0,1}IPV4ADDR|  # ::255.255.255.255  ::ffff:255.255.255.255  ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
           (IPV6SEG:){1,4}:IPV4ADDR               # 2001:db8:3:4::192.0.2.33  64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address)
           )

我在GitHub上发布了一个脚本来测试正则表达式:https://gist.github.com/syzdek/6086792


以下内容将验证IPv4,IPv6(完整和压缩)和IPv6v4(完整和压缩)地址:

1
'/^(?>(?>([a-f0-9]{1,4})(?>:(?1)){7}|(?!(?:.*[a-f0-9](?>:|$)){8,})((?1)(?>:(?1)){0,6})?::(?2)?)|(?>(?>(?1)(?>:(?1)){5}:|(?!(?:.*[a-f0-9]:){6,})(?3)?::(?>((?1)(?>:(?1)){0,4}):)?)?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?4)){3}))$/iD'

来自" IPv6正则表达式":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(\A([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6}\Z)|
(\A([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}\Z)|
(\A([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}\Z)|
(\A([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}\Z)|
(\A([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}\Z)|
(\A([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1}\Z)|
(\A(([0-9a-f]{1,4}:){1,7}|:):\Z)|
(\A:(:[0-9a-f]{1,4}){1,7}\Z)|
(\A((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})\Z)|
(\A(([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})\Z)|
(\A([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)|
(\A([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)|
(\A([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)|
(\A([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)|
(\A([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)|
(\A(([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)|
(\A:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z)

听起来您可能正在使用Python。如果是这样,您可以使用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
import socket

def check_ipv6(n):
    try:
        socket.inet_pton(socket.AF_INET6, n)
        return True
    except socket.error:
        return False

print check_ipv6('::1') # True
print check_ipv6('foo') # False
print check_ipv6(5)     # TypeError exception
print check_ipv6(None)  # TypeError exception

我认为您不必将IPv6编译到Python中即可获得inet_pton,如果您将socket.AF_INET作为第一个参数传递,它也可以解析IPv4地址。注意:这可能不适用于非Unix系统。


我必须坚决支持弗兰克·克鲁格的回答。

虽然您说您需要一个正则表达式来匹配IPv6地址,但我假设您真正需要的是能够检查给定的字符串是否为有效的IPv6地址。这里有一个微妙但重要的区别。

有多种方法可以检查给定字符串是否为有效的IPv6地址,而正则表达式匹配只是一种解决方案。

如果可以,请使用现有的库。该库将具有较少的错误,并且其使用将减少需要维护的代码。

Factor Mystic建议的正则表达式长而复杂。它很可能有效,但是您还应该考虑如果意外失败,应该如何应对。我要在此处说明的要点是,如果您自己无法形成所需的正则表达式,则将无法轻松调试它。

如果没有合适的库,最好编写自己的不依赖于正则表达式的IPv6验证例程。如果您编写它,则说明您理解它;如果您理解,则可以添加注释以对其进行解释,以便其他人也可以理解并随后对其进行维护。

使用正则表达式时,请谨慎行事,而您无法向他人解释其功能。


我不是Ipv6专家,但我认为您可以通过以下方法更轻松地获得不错的结果:

1
^([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{1,4}$|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})$

回答"是有效的ipv6",对我来说似乎还可以。要将它分解成一部分...算了。我省略了未指定的(::),因为在我的数据库中没有"未指定的地址"。

开始:
^([0-9A-Fa-f]{0,4}:){2,7} <-匹配可压缩部分,我们可以将其翻译为:2到7之间的冒号,中间可能有十六进制数字。

其次是:
[0-9A-Fa-f]{1,4}$ <-十六进制数字(省略前导0) 要么 ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4} <-IPV4地址


这个正则表达式将根据GNU C ++ regex的实现与有效的IPv6和IPv4地址进行匹配,并使用REGULAR EXTENDED模式:

1
"^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*$"

这也捕获了loopback(:: 1)和ipv6地址。
将{}更改为+并将:放在第一个方括号内。

1
([A-f0-9:]+:+)+[A-f0-9]+

使用ifconfig -a输出进行测试
http://regexr.com/

Unix或Mac OSx终端o选项仅返回匹配的输出(ipv6),包括:: 1

1
ifconfig -a | egrep -o '([A-f0-9:]+:+)+[A-f0-9]+'

获取所有IP地址(IPv4或IPv6)并在Unix OSx上打印匹配项

1
ifconfig -a | egrep -o '([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) | (([A-f0-9:]+:+)+[A-f0-9]+)'

谨防!在Java中,使用InetAddress和相关类(Inet4Address,Inet6Address,URL)可能会涉及网络流量!例如。 DNS解析(URL.equals,InetAddress来自字符串!)。该通话可能需要很长时间,并且阻塞了!

对于IPv6,我有类似的内容。当然,这不会处理IPv6的非常细微的细节,例如仅在某些类别的IPv6地址上允许区域索引。而且此正则表达式不是为组捕获而编写的,它只是"匹配"类型的正则表达式。

S-IPv6段= [0-9a-f]{1,4}

I-IPv4 = (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})

示意图(第一部分将IPv6地址与后缀IPv4匹配,第二部分将IPv6地址匹配,最后是区域索引):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(
(
::(S:){0,5}|
S::(S:){0,4}|
(S:){2}:(S:){0,3}|
(S:){3}:(S:){0,2}|
(S:){4}:(S:)?|
(S:){5}:|
(S:){6}
)
I

|

:(:|(:S){1,7})|
S:(:|(:S){1,6})|
(S:){2}(:|(:S){1,5})|
(S:){3}(:|(:S){1,4})|
(S:){4}(:|(:S){1,3})|
(S:){5}(:|(:S){1,2})|
(S:){6}(:|(:S))|
(S:){7}:|
(S:){7}S
)

(?:%[0-9a-z]+)?

在这里,可能的正则表达式(不区分大小写,被行的开头/结尾等所需要的内容所包围,等等):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(?:
(?:
::(?:[0-9a-f]{1,4}:){0,5}|
[0-9a-f]{1,4}::(?:[0-9a-f]{1,4}:){0,4}|
(?:[0-9a-f]{1,4}:){2}:(?:[0-9a-f]{1,4}:){0,3}|
(?:[0-9a-f]{1,4}:){3}:(?:[0-9a-f]{1,4}:){0,2}|
(?:[0-9a-f]{1,4}:){4}:(?:[0-9a-f]{1,4}:)?|
(?:[0-9a-f]{1,4}:){5}:|
(?:[0-9a-f]{1,4}:){6}
)
(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})\.){3}
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})|

:(?::|(?::[0-9a-f]{1,4}){1,7})|
[0-9a-f]{1,4}:(?::|(?::[0-9a-f]{1,4}){1,6})|
(?:[0-9a-f]{1,4}:){2}(?::|(?::[0-9a-f]{1,4}){1,5})|
(?:[0-9a-f]{1,4}:){3}(?::|(?::[0-9a-f]{1,4}){1,4})|
(?:[0-9a-f]{1,4}:){4}(?::|(?::[0-9a-f]{1,4}){1,3})|
(?:[0-9a-f]{1,4}:){5}(?::|(?::[0-9a-f]{1,4}){1,2})|
(?:[0-9a-f]{1,4}:){6}(?::|(?::[0-9a-f]{1,4}))|
(?:[0-9a-f]{1,4}:){7}:|
(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}
)

(?:%[0-9a-z]+)?


如果您使用Perl,请尝试使用Net :: IPv6Addr

1
2
3
4
5
6
use Net::IPv6Addr;

if( defined Net::IPv6Addr::is_ipv6($ip_address) ){
  print"Looks like an ipv6 address
";
}

NetAddr :: IP

1
2
3
use NetAddr::IP;

my $obj = NetAddr::IP->new6($ip_address);

验证:: IP

1
2
3
4
5
6
use Validate::IP qw'is_ipv6';

if( is_ipv6($ip_address) ){
  print"Looks like an ipv6 address
";
}

一个简单的正则表达式可以匹配,但是我不建议任何形式的验证是这样的:

1
([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}

请注意,这与地址中的任何位置的压缩匹配,尽管它与环回地址:: 1不匹配。我发现这是一个合理的折衷,以使正则表达式保持简单。

我在iTerm2智能选择规则中成功使用了它,以四击IPv6地址。


在Scala中,使用众所周知的Apache Commons验证器。

http://mvnrepository.com/artifact/commons-validator/commons-validator/1.4.1

1
2
3
4
5
6
7
8
9
10
11
12
libraryDependencies +="commons-validator" %"commons-validator" %"1.4.1"


import org.apache.commons.validator.routines._

/**
 * Validates if the passed ip is a valid IPv4 or IPv6 address.
 *
 * @param ip The IP address to validate.
 * @return True if the passed IP address is valid, false otherwise.
 */  
 def ip(ip: String) = InetAddressValidator.getInstance().isValid(ip)

按照方法ip(ip: String)的测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"The `ip` validator" should {
 "return false if the IPv4 is invalid" in {
    ip("123") must beFalse
    ip("255.255.255.256") must beFalse
    ip("127.1") must beFalse
    ip("30.168.1.255.1") must beFalse
    ip("-1.2.3.4") must beFalse
  }

 "return true if the IPv4 is valid" in {
    ip("255.255.255.255") must beTrue
    ip("127.0.0.1") must beTrue
    ip("0.0.0.0") must beTrue
  }

  //IPv6
  //@see: http://www.ronnutter.com/ipv6-cheatsheet-on-identifying-valid-ipv6-addresses/
 "return false if the IPv6 is invalid" in {
    ip("1200::AB00:1234::2552:7777:1313") must beFalse
  }

 "return true if the IPv6 is valid" in {
    ip("1200:0000:AB00:1234:0000:2552:7777:1313") must beTrue
    ip("21DA:D3:0:2F3B:2AA:FF:FE28:9C5A") must beTrue
  }
}

以下正则表达式仅适用于IPv6。组1与IP匹配。

1
(([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4})

使用Ruby?尝试这个:

1
/^(((?=.*(::))(?!.*\3.+\3))\3?|[\dA-F]{1,4}:)([\dA-F]{1,4}(\3|:\b)|\2){5}(([\dA-F]{1,4}(\3|:\b|$)|\2){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/i


查看其他答案中包含的模式,可以通过引用组和利用超前行为来改善许多好的模式。这是一个自引用模式的示例,如果需要,我将在PHP中使用该模式:

1
2
3
4
5
6
7
8
9
10
^(?<hgroup>(?<hex>[[:xdigit:]]{0,4}) # grab a sequence of up to 4 hex digits
                                     # and name this pattern for usage later
     (?<!:::):{1,2})                 # match 1 or 2 ':' characters
                                     # as long as we can't match 3
 (?&hgroup){1,6} # match our hex group 1 to 6 more times
 (?:(?:
    # match an ipv4 address or
    (?<dgroup>2[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(?&dgroup)
    # match our hex group one last time
    |(?&hex))$

注意:PHP为此有一个内置的过滤器,这将是一个更好的解决方案
图案。

Regex101分析


InetAddressUtils已定义了所有模式。我最终直接使用了他们的模式,并将其粘贴在这里以供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static final String IPV4_BASIC_PATTERN_STRING =
       "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + // initial 3 fields, 0-255 followed by .
        "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255

private static final Pattern IPV4_PATTERN =
    Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING +"$");

private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros
        Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING +"$");

private static final Pattern IPV6_STD_PATTERN =
    Pattern.compile(
           "^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");

private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
    Pattern.compile(
           "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields
            "::" +
            "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields

根据您的需求,近似如下所示:

1
[0-9a-f:]+

可能就足够了(例如,与简单的日志文件grepping一样)。


很难找到适用于所有IPv6情况的正则表达式。它们通常很难维护,不容易阅读,并且可能导致性能问题。因此,我想分享一个我开发的替代解决方案:用于IPv6的正则表达式(RegEx)与IPv4分开

现在您可能会问:"此方法只能找到IPv6,如何在文本或文件中找到IPv6?"这里也有解决此问题的方法。

注意:如果您不想在.NET中使用IPAddress类,也可以将其替换为我的方法。它还涵盖了映射的IPv4和特殊情况,而IPAddress没有涵盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
class IPv6
{
    public List<string> FindIPv6InFile(string filePath)
    {
        Char ch;
        StringBuilder sbIPv6 = new StringBuilder();
        List<string> listIPv6 = new List<string>();
        StreamReader reader = new StreamReader(filePath);
        do
        {
            bool hasColon = false;
            int length = 0;

            do
            {
                ch = (char)reader.Read();

                if (IsEscapeChar(ch))
                    break;

                //Check the first 5 chars, if it has colon, then continue appending to stringbuilder
                if (!hasColon && length < 5)
                {
                    if (ch == ':')
                    {
                        hasColon = true;
                    }
                    sbIPv6.Append(ch.ToString());
                }
                else if (hasColon) //if no colon in first 5 chars, then dont append to stringbuilder
                {
                    sbIPv6.Append(ch.ToString());
                }

                length++;

            } while (!reader.EndOfStream);

            if (hasColon && !listIPv6.Contains(sbIPv6.ToString()) && IsIPv6(sbIPv6.ToString()))
            {
                listIPv6.Add(sbIPv6.ToString());
            }

            sbIPv6.Clear();

        } while (!reader.EndOfStream);
        reader.Close();
        reader.Dispose();

        return listIPv6;
    }

    public List<string> FindIPv6InText(string text)
    {
        StringBuilder sbIPv6 = new StringBuilder();
        List<string> listIPv6 = new List<string>();

        for (int i = 0; i < text.Length; i++)
        {
            bool hasColon = false;
            int length = 0;

            do
            {
                if (IsEscapeChar(text[length + i]))
                    break;

                //Check the first 5 chars, if it has colon, then continue appending to stringbuilder
                if (!hasColon && length < 5)
                {
                    if (text[length + i] == ':')
                    {
                        hasColon = true;
                    }
                    sbIPv6.Append(text[length + i].ToString());
                }
                else if (hasColon) //if no colon in first 5 chars, then dont append to stringbuilder
                {
                    sbIPv6.Append(text[length + i].ToString());
                }

                length++;

            } while (i + length != text.Length);

            if (hasColon && !listIPv6.Contains(sbIPv6.ToString()) && IsIPv6(sbIPv6.ToString()))
            {
                listIPv6.Add(sbIPv6.ToString());
            }

            i += length;
            sbIPv6.Clear();
        }

        return listIPv6;
    }

    bool IsEscapeChar(char ch)
    {
        if (ch != ' ' && ch != '
' && ch != '
' && ch!='\t')
        {
            return false;
        }

        return true;
    }

    bool IsIPv6(string maybeIPv6)
    {
        IPAddress ip;
        if (IPAddress.TryParse(maybeIPv6, out ip))
        {
            return ip.AddressFamily == AddressFamily.InterNetworkV6;
        }
        else
        {
            return false;
        }
    }

}

在Java中,可以使用库类sun.net.util.IPAddressUtil

1
IPAddressUtil.isIPv6LiteralAddress(iPaddress);


这将适用于IPv4和IPv6:

1
^(([0-9a-f]{0,4}:){1,7}[0-9a-f]{1,4}|([0-9]{1,3}\.){3}[0-9]{1,3})$

您可以使用我为此目的制作的ipextract shell工具。它们基于regexp和grep。

用法:

1
2
3
4
$ ifconfig | ipextract6
fe80::1%lo0
::1
fe80::7ed1:c3ff:feec:dee1%en0

这是我想出的,使用一些先行和命名组。这当然只是IPv6,但如果要添加IPv4,它不应干扰其他模式:

1
(?=([0-9a-f]+(:[0-9a-f])*)?(?P<wild>::)(?!([0-9a-f]+:)*:))(::)?([0-9a-f]{1,4}:{1,2}){0,6}(?(wild)[0-9a-f]{0,4}|[0-9a-f]{1,4}:[0-9a-f]{1,4})

我使用python生成了以下代码,并与re模块一起使用。前瞻性断言可确保在地址中出现正确数量的点或冒号。它不支持IPv6表示法中的IPv4。

1
2
3
pattern = '^(?=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$)(?:(?:25[0-5]|[12][0-4][0-9]|1[5-9][0-9]|[1-9]?[0-9])\.?){4}$|(?=^(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}$)(?![^:]*::.+::[^:]*$)(?:(?=.*::.*)|(?=\w+:\w+:\w+:\w+:\w+:\w+:\w+:\w+))(?:(?:^|:)(?:[0-9a-f]{4}|[1-9a-f][0-9a-f]{0,3})){0,8}(?:::(?:[0-9a-f]{1,4}(?:$|:)){0,6})?$'
result = re.match(pattern, ip)
if result: result.group(0)

只需匹配来自原点的本地方括号(包括方括号)即可。我知道它不那么全面,但是在javascript中其他代码很难追踪主要是无法正常工作的问题,因此这似乎让我了解了我现在需要的东西。也不需要多余的字母A-F。

1
^\[([0-9a-fA-F]{1,4})(\:{1,2})([0-9a-fA-F]{1,4})(\:{1,2})([0-9a-fA-F]{1,4})(\:{1,2})([0-9a-fA-F]{1,4})(\:{1,2})([0-9a-fA-F]{1,4})\]

Jinnko的版本已简化,我认为更好。


如上所述,获得IPv6文本表示形式的验证解析器的另一种方法是使用编程。这是一个完全符合RFC-4291和RFC-5952的文件。我已经用ANSI C编写了这段代码(与GCC一起使用,在Linux上通过了测试-与clang一起使用,在FreeBSD上通过了测试)。因此,它仅依赖于ANSI C标准库,因此可以在任何地方进行编译(我已经在FreeBSD的内核模块中将其用于IPv6解析)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// IPv6 textual representation validating parser fully compliant with RFC-4291 and RFC-5952
// BSD-licensed / Copyright 2015-2017 Alexandre Fenyo

#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

typedef enum { false, true } bool;

static const char hexdigits[] ="0123456789abcdef";
static int digit2int(const char digit) {
  return strchr(hexdigits, digit) - hexdigits;
}

// This IPv6 address parser handles any valid textual representation according to RFC-4291 and RFC-5952.
// Other representations will return -1.
//
// note that str input parameter has been modified when the function call returns
//
// parse_ipv6(char *str, struct in6_addr *retaddr)
// parse textual representation of IPv6 addresses
// str:     input arg
// retaddr: output arg
int parse_ipv6(char *str, struct in6_addr *retaddr) {
  bool compressed_field_found = false;
  unsigned char *_retaddr = (unsigned char *) retaddr;
  char *_str = str;
  char *delim;

  bzero((void *) retaddr, sizeof(struct in6_addr));
  if (!strlen(str) || strchr(str, ':') == NULL || (str[0] == ':' && str[1] != ':') ||
      (strlen(str) >= 2 && str[strlen(str) - 1] == ':' && str[strlen(str) - 2] != ':')) return -1;

  // convert transitional to standard textual representation
  if (strchr(str, '.')) {
    int ipv4bytes[4];
    char *curp = strrchr(str, ':');
    if (curp == NULL) return -1;
    char *_curp = ++curp;
    int i;
    for (i = 0; i < 4; i++) {
      char *nextsep = strchr(_curp, '.');
      if (_curp[0] == '0' || (i < 3 && nextsep == NULL) || (i == 3 && nextsep != NULL)) return -1;
      if (nextsep != NULL) *nextsep = 0;
      int j;
      for (j = 0; j < strlen(_curp); j++) if (_curp[j] < '0' || _curp[j] > '9') return -1;
      if (strlen(_curp) > 3) return -1;
      const long val = strtol(_curp, NULL, 10);
      if (val < 0 || val > 255) return -1;
      ipv4bytes[i] = val;
      _curp = nextsep + 1;
    }
    sprintf(curp,"%x%02x:%x%02x", ipv4bytes[0], ipv4bytes[1], ipv4bytes[2], ipv4bytes[3]);
  }

  // parse standard textual representation
  do {
    if ((delim = strchr(_str, ':')) == _str || (delim == NULL && !strlen(_str))) {
      if (delim == str) _str++;
      else if (delim == NULL) return 0;
      else {
        if (compressed_field_found == true) return -1;
        if (delim == str + strlen(str) - 1 && _retaddr != (unsigned char *) (retaddr + 1)) return 0;
        compressed_field_found = true;
        _str++;
        int cnt = 0;
        char *__str;
        for (__str = _str; *__str; ) if (*(__str++) == ':') cnt++;
        unsigned char *__retaddr = - 2 * ++cnt + (unsigned char *) (retaddr + 1);
        if (__retaddr <= _retaddr) return -1;
        _retaddr = __retaddr;
      }
    } else {
      char hexnum[4] ="0000";
      if (delim == NULL) delim = str + strlen(str);
      if (delim - _str > 4) return -1;
      int i;
      for (i = 0; i < delim - _str; i++)
        if (!isxdigit(_str[i])) return -1;
        else hexnum[4 - (delim - _str) + i] = tolower(_str[i]);
      _str = delim + 1;
      *(_retaddr++) = (digit2int(hexnum[0]) << 4) + digit2int(hexnum[1]);
      *(_retaddr++) = (digit2int(hexnum[2]) << 4) + digit2int(hexnum[3]);
    }
  } while (_str < str + strlen(str));
  return 0;
}

当您考虑带有嵌入式ipv4的地址和压缩的地址时,ipv6的正则表达式会变得非常棘手,如您从这些答案中看到的那样。

开源IPAddress Java库将验证IPv6和IPv4的所有标准表示形式,并且还支持前缀长度(及其验证)。免责声明:我是该库的项目经理。

代码示例:

1
2
3
4
5
6
7
8
9
10
        try {
            IPAddressString str = new IPAddressString("::1");
            IPAddress addr = str.toAddress();
            if(addr.isIPv6() || addr.isIPv6Convertible()) {
                IPv6Address ipv6Addr = addr.toIPv6();
            }
            //use address
        } catch(AddressStringException e) {
            //e.getMessage has validation error
        }

对于PHP 5.2+用户filter_var效果很好。

我知道这并不能回答最初的问题(特别是正则表达式解决方案),但我希望将其发布,以期将来对其他人有所帮助。

1
2
$is_ip4address = (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== FALSE);
$is_ip6address = (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE);

试试这个小单线。它只能匹配有效的未压缩/压缩的IPv6地址(不能匹配IPv4混合地址)

1
/(?!.*::.*::)(?!.*:::.*)(?!:[a-f0-9])((([a-f0-9]{1,4})?[:](?!:)){7}|(?=(.*:[:a-f0-9]{1,4}::|^([:a-f0-9]{1,4})?::))(([a-f0-9]{1,4})?[:]{1,2}){1,6})[a-f0-9]{1,4}/

如果您只需要普通的IP-s(没有斜杠),请在这里:

1
^(?:[0-9a-f]{1,4}(?:::)?){0,7}::[0-9a-f]+$

我在主机文件编辑器应用程序中将其用于语法荧光笔。作为魅力。


正则表达式允许在IPv4部分中使用前导零。

一些Unix和Mac发行版将这些段转换为八进制。

我建议使用25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d作为IPv4段。


推荐阅读

    linux获取地址命令?

    linux获取地址命令?,地址,网络,信息,系统,设备,终端,命令,中心,管理,数字,lin

    linux获取地址命令?

    linux获取地址命令?,地址,网络,信息,系统,设备,终端,命令,中心,管理,数字,lin

    linux命令物理地址?

    linux命令物理地址?,地址,信息,电脑,系统,网络,服务,网卡,数据,软件,手机,lin

    linux地址转换命令是?

    linux地址转换命令是?,地址,系统,代码,密码,网络,信息,服务,电脑,设备,报告,

    linux减压缩包命令?

    linux减压缩包命令?,系统,工具,文件,手机,命令,格式,文件名,终端,浏览器,意

    linux的ip地址命令?

    linux的ip地址命令?,地址,系统,网络,信息,数字,电脑,终端,命令,服务,名称,Lin

    linux命令设置扩展屏?

    linux命令设置扩展屏?,工具,系统,电脑,技术,软件,装备,情况,工作,命令,显示

    linux命令行空格匹配?

    linux命令行空格匹配?,系统,电脑,地址,空格,短线,名称,服务,网络,工具,引号,

    linux解压缩的命令是?

    linux解压缩的命令是?,工具,系统,命令,文件,名称,电脑,平台,文件名,目录,格

    linux中递归压缩命令?

    linux中递归压缩命令?,系统,工作,命令,工具,管理,信息,设备,文件,目录,常用

    linux磁盘扩展命令?

    linux磁盘扩展命令?,在线,信息,扩大,风险,磁盘,虚拟机,容量,分区,目录,链接,L

    linux修改地址命令?

    linux修改地址命令?,地址,系统,代码,网络,服务,信息,密码,命令,管理,基础,lin

    linux压缩命令实验?

    linux压缩命令实验?,系统,文件,命令,名称,地址,工具,手机,目录,文件名,格式,t

    linux命令压缩目录?

    linux命令压缩目录?,系统,图片,地址,文件,工具,管理,位置,电脑,软件,名称,在L

    linux查看原压缩命令?

    linux查看原压缩命令?,系统,网络,最新,时间,软件,数字,设备,命令,文件,指令,L

    linux压缩命令与解压?

    linux压缩命令与解压?,系统,命令,管理,平台,电脑,文件,目录,文件名,选项,下

    linux查看命令的地址?

    linux查看命令的地址?,地址,系统,网络,信息,设备,电脑,手机,终端,技术,环境,l

    linux地址查询命令?

    linux地址查询命令?,地址,网络,系统,工作,信息,设备,名称,标准,手机,管理,lin

    linux逐个压缩命令?

    linux逐个压缩命令?,系统,网络,设备,时间,文件,命令,目录,备份,后缀,程序,Lin

    linux压缩命令解压?

    linux压缩命令解压?,命令,平台,电脑,名称,工具,文件,目录,文件名,文件夹,终