谨防! In Java, the use of InetAddress and related classes (Inet4Address, Inet6Address, URL) may involve network trafic! 例如DNS解析(URL.equals,来自字符串的InetAddress!) . 这个电话可能需要很长时间才能阻止!
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) 的测试:
"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
}
}
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 != '\r' && ch != '\n' && 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;
}
}
}
^(?<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))$
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)
如上所述,获得IPv6文本表示的另一种方法是使用编程 . 这是一个完全符合RFC-4291和RFC-5952的版本 . 我've written this code in ANSI C (works with GCC, passed tests on Linux - works with clang, passed tests on FreeBSD). Thus, it does only rely on the ANSI C standard library, so it can be compiled everywhere (I'已使用它在FreeBSD内核模块中进行IPv6解析 .
// 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;
}
30 回答
对于PHP 5.2用户
filter_var
效果很好 .我知道这不回答原始问题(特别是正则表达式解决方案),但我发布这个希望它可能在将来帮助其他人 .
这适用于IPv4和IPv6:
我不是Ipv6专家,但我认为你可以用这个更轻松地获得相当不错的结果:
回答“是一个有效的ipv6”,它对我来说似乎没问题 . 把它分解成几部分......忘掉它吧 . 我省略了未指定的一个(::),因为我的数据库中没有“未指定的地址” .
开头:
^([0-9A-Fa-f]{0,4}:){2,7}
< - 匹配可压缩部分,我们可以将其翻译为:2到7之间的冒号,它们之间可能有heaxadecimal数字 .接下来是:
[0-9A-Fa-f]{1,4}$
< - 一个十六进制数字(省略前导0)或((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}
< - 一个Ipv4地址如果我可以解决您的问题,请考虑使用您的网络库的地址概念来解析和检查错误 .
我想在某些时候你会想要对这些地址做些什么,那么为什么不直接找到源代码并确保你的网络库能够理解地址呢?这比仅仅希望在这里发布的正则表达式更符合您的实现的地址概念更好 .
在Java中我们有
InetAddress
. 在.NET中我们有IPAddress
. 在.NET中,你甚至可以TryParse on the IPAddress class为你做这个测试!使用Ruby?试试这个:
根据您的需要,近似如下:
可能就足够了(例如,简单的日志文件grepping) .
我无法得到@Factor Mystic使用POSIX正则表达式的答案,所以我写了一个与POSIX正则表达式和PERL正则表达式一起使用的答案 .
它应匹配:
IPv6地址
零压缩IPv6地址(section 2.2 of rfc5952)
带有区域索引的
链接本地IPv6地址(section 11 of rfc4007)
IPv4嵌入式IPv6地址(section 2 of rfc6052)
IPv4映射的IPv6地址(section 2.1 of rfc2765)
IPv4转换地址(section 2.1 of rfc2765)
IPv6正则表达式:
为了便于阅读,以下是将主要OR点分割成单独行的上述正则表达式:
为了使上述更容易理解,以下“伪”代码复制了上述内容:
我在GitHub上发布了一个测试正则表达式的脚本:https://gist.github.com/syzdek/6086792
以下将验证IPv4,IPv6(完整和压缩)以及IPv6v4(完整和压缩)地址:
来自“IPv6 regex”:
听起来你可能在使用Python . 如果是这样,你可以使用这样的东西:
我认为您不必将Python编译到Python中以获取inet_pton,如果您传入
socket.AF_INET
作为第一个参数,它也可以解析IPv4地址 . 注意:这可能不适用于非Unix系统 .我必须强烈推断Frank Krueger的回答 .
虽然你说你需要一个正则表达式来匹配IPv6地址,但我假设你真正需要的是能够检查一个给定的字符串是否是一个有效的IPv6地址 . 这里有一个微妙但重要的区别 .
有多种方法可以检查给定字符串是否是有效的IPv6地址,而正则表达式匹配只是一种解决方案 .
如果可以,请使用现有库 . 该库将有更少的错误,它的使用将导致更少的代码供您维护 .
Factor Mystic建议的正则表达式冗长而复杂 . 它很可能有效,但您还应该考虑如果您能够轻松地调试它,那么您在这里尝试的方式是什么 .
如果您没有合适的库,那么编写自己的IPv6验证例程(不依赖于正则表达式)可能会更好 . 如果您编写它,您就会理解它,如果您理解它,您可以添加注释来解释它,以便其他人也可以理解并随后维护它 .
使用正则表达式时要谨慎行事,其功能无法向其他人解释 .
此正则表达式将根据使用REGULAR EXTENDED模式的正则表达式的GNU C实现匹配有效的IPv6和IPv4地址:
正则表达式仅适用于IPv6 . 第1组与IP匹配 .
在Java中,您可以使用库类
sun.net.util.IPAddressUtil
:谨防! In Java, the use of InetAddress and related classes (Inet4Address, Inet6Address, URL) may involve network trafic! 例如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})
原理图(第一部分使用IPv4后缀匹配IPv6地址,第二部分匹配IPv6地址,最后一部分匹配区域索引):
在这里可能正则表达式(不区分大小写,包围所需的内容,如行的开头/结尾等):
如果你使用Perl试试Net::IPv6Addr
NetAddr::IP
Validate::IP
这也捕获了loopback(:: 1)和ipv6地址 . 将{}更改为并放置在第一个方括号内 .
用ifconfig -a输出测试http://regexr.com/
Unix或Mac OSx终端o选项仅返回匹配的输出(ipv6),包括:: 1
获取所有IP地址(IPv4或IPv6)并在unix OSx术语上打印匹配
在 Scala 中使用着名的Apache Commons验证器 .
http://mvnrepository.com/artifact/commons-validator/commons-validator/1.4.1
按照方法
ip(ip: String)
的测试:一个简单的正则表达式将匹配,但我不建议任何类型的验证是这样的:
请注意,这与地址中的任何位置的压缩匹配,但它与环回地址:: 1不匹配 . 我发现这是一个合理的妥协,以保持正则表达式简单 .
我成功地在iTerm2智能选择规则中使用它来四击IPv6地址 .
很难找到适用于所有IPv6情况的正则表达式 . 它们通常难以维护,不易读取并可能导致性能问题 . 因此,我想分享一个我开发的替代解决方案:Regular Expression (RegEx) for IPv6 Separate from IPv4
现在您可能会问“此方法只能找到IPv6,如何在文本或文件中找到IPv6?”以下是此问题的方法 .
Note :如果您不想在.NET中使用IPAddress类,也可以将其替换为my method . 它还涵盖了映射的IPv4和特殊情况,而IPAddress没有涵盖 .
查看其他答案中包含的模式,可以通过引用组和使用前瞻来改进许多良好的模式 . 下面是一个自我引用的模式示例,如果我不得不:我将在PHP中使用它:
Note: PHP有一个内置的过滤器,这是一个比这个模式更好的解决方案 .
Regex101 Analysis
InetAddressUtils
已定义所有模式 . 我最终直接使用他们的模式,并将其粘贴在这里以供参考:这是我想出来的,使用一些前瞻和命名组 . 这当然只是IPv6,但如果要添加IPv4,则不应干扰其他模式:
您可以使用我为此目的制作的the ipextract shell tools . 它们基于regexp和grep .
用法:
我使用python生成以下内容并使用re模块 . 前瞻断言确保地址中出现正确数量的点或冒号 . 它不支持IPv6表示法中的IPv4 .
只需将包含方括号的原点与原点相匹配 . 我知道它不是那么全面,但在javascript中,其他人很难追踪主要是不工作的问题,所以这似乎让我得到了我现在所需要的东西 . 额外的首都A-F也不需要 .
Jinnko的版本是简化的,我看得更好 .
如上所述,获得IPv6文本表示的另一种方法是使用编程 . 这是一个完全符合RFC-4291和RFC-5952的版本 . 我've written this code in ANSI C (works with GCC, passed tests on Linux - works with clang, passed tests on FreeBSD). Thus, it does only rely on the ANSI C standard library, so it can be compiled everywhere (I'已使用它在FreeBSD内核模块中进行IPv6解析 .
当您考虑使用嵌入式ipv4的地址和压缩的地址时,ipv6的正则表达起来会非常棘手,正如您从其中一些答案中可以看到的那样 .
The open-source IPAddress Java library将验证IPv6和IPv4的所有标准表示,并且还支持前缀长度(以及此类验证) . 免责声明:我是该图书馆的项目经理 .
代码示例:
尝试这个小单线 . 它应该只匹配有效的未压缩/压缩的IPv6地址(没有IPv4混合)
正则表达式允许在IPv4部分中使用前导零 .
一些Unix和Mac发行版将这些段转换为八进制 .
我建议使用
25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d
作为IPv4段 .