标记为重复之前请先阅读
我无法创建或找到适用于所有IPv6格式的RegEx(我的测试用例如下)。我知道每个人都指向这个问题:匹配有效IPv6地址的正则表达式但是,它们都将IPv6与IPv4结合在一起和/或不适用于所有测试用例。
要求:
我不想让它也验证IPv4值,我已经有一个单独的IPv4验证功能。
我需要在Coldfusion中工作的模式和在PL/SQL中工作的模式。
因为我在PL/SQL中使用它,所以它的模式必须少于512个字符。而且Oracle仅支持RegExp语言的一小部分。因此Coldfusion模式最终可能会与PL/SQL模式不同,只要它们都可以工作。
最终结果没有长的RegEx,可以将其拆分。
这是我正在尝试的最新模式:
^(?>(?>([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}))$
对于Coldfusion,此值接近,但不是100%。在PL/SQL中根本不起作用。
测试结果http://regex101.com/r/wI8cI0粗体项是该模式在Coldfusion中不起作用的项:
比赛
比赛
比赛
比赛
比赛
匹配(但是@Michael Hampton说这不应该匹配,因为它不是有效的IPv6地址,但是其他人告诉我它是有效的,所以我不确定这个测试用例。)
匹配(::实际上是有效格式,感谢@Sander Steffann。)
比赛
没有匹配
比赛
没有匹配
没有匹配
没有匹配
比赛
比赛
没有匹配
没有匹配
没有匹配
没有匹配
我从以下网址获得了测试案例8-11:http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzai2%2Frzai2ipv6addrformat.htm
并被告知:测试9和11是针对IPv6地址前缀的,而不是IPv6地址,因此它们不应匹配。
最终结果,我需要它们在这样的语句中工作:
ColdFusion:
(?>([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}))$",VALUE[i])>
PL / SQL:
IF ( REGEXP_LIKE(v,'^(?>(?>([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}))$','i') ) THEN
您的商品6无效。 正则表达式是正确的。 您的测试用例不是IPv4映射的IPv6地址的有效示例。 修复测试用例。
好。 做了一些进一步的研究,我认为第6项不是您所说的有效格式,但是这些是有效格式,但是RegEx却说它们不是:(这两种格式允许IPv6应用程序直接与IPv4应用程序通信)0:0:0:0:0:ffff:192.1.56.10和(以下两种格式用于隧道传输。它允许IPv6节点在IPv4基础结构之间进行通信)0:0:0:0:0:0:192.1.56.10和::192.1.56.1096(来自:publib.boulder.ibm.com/infocenter/iseries/v5r3/)
您在最终评论中提供的最后两个曾经是有效的,但是今天已经不再使用。 您链接的文档已有十多年的历史了...
不要将正则表达式用于IPv6解析。 这是一场噩梦。
据我研究,尚没有适用于所有IPv6格式的RegEx。即使有,它也是如此复杂且难以维护(不易阅读)。此外,它也可能导致性能问题。因此,我决定为此编写一种方法(函数)。您也可以根据需要轻松添加任何特殊情况。我已经用C#编写了代码,但是我认为您可以将此算法转换为任何语言:
class IPv6Validator
{
string charValidator = @"[A-Fa-f0-9]";
string IPv4Validation = @"^((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]?)$";
PUBLIC bool IsIPv6(string maybeIPv6)
{
IF (maybeIPv6 =="::")
{
RETURN TRUE;
}
int numberOfEmptyDigitGroups = 0;
int expectedDigitGroupsLength = 8;
string[] arrMaybeIPv6 = maybeIPv6.Split(':');
IF (arrMaybeIPv6.LENGTH > 9 || arrMaybeIPv6.LENGTH < 3)
{
RETURN FALSE;
}
FOR (int i = 0; i < arrMaybeIPv6.LENGTH; i++)
{
//IF IPv6 starts OR ends WITH"::" (ex ::1)
IF ((i == 0 || i == arrMaybeIPv6.LENGTH - 2) && IsEmptyDigitGroup(arrMaybeIPv6[i]) && IsEmptyDigitGroup(arrMaybeIPv6[i+1]))
{
expectedDigitGroupsLength = 9;
numberOfEmptyDigitGroups++;
i++;
}
ELSE IF (arrMaybeIPv6[i].TRIM() == string.Empty) //IF IPv6 contains :: (ex 1:2::3)
{
numberOfEmptyDigitGroups++;
}
//Cannot have more than one"::" (ex ::1:2::3)
IF (numberOfEmptyDigitGroups > 1)
{
RETURN FALSE;
}
//Mapped IPv4 control
IF (i == arrMaybeIPv6.LENGTH - 1 && IsIPv4(arrMaybeIPv6[i]) && arrMaybeIPv6.LENGTH < 8)
{
RETURN TRUE;
}
ELSE IF (i == arrMaybeIPv6.LENGTH - 1 && HasSpecialCharInIPv6(arrMaybeIPv6[i], IsEmptyDigitGroup(arrMaybeIPv6[i - 1]))) //IF LAST digit GROUP contains special CHAR (ex fe80::3%eth0)
{
RETURN TRUE;
}
ELSE //IF NOT IPV4, CHECK the digits
{
//Cannot have more than 4 digits (ex 12345:1::)
IF (arrMaybeIPv6[i].LENGTH > 4)
{
RETURN FALSE;
}
//CHECK IF it has unvalid CHAR
foreach (CHAR ch IN arrMaybeIPv6[i])
{
IF (!IsIPv6Char(ch.ToString()))
{
RETURN FALSE;
}
}
}
//Checks IF it has extra digit (ex 1:2:3:4:5:6:7:8f:)
IF (i >= expectedDigitGroupsLength)
{
RETURN FALSE;
}
//IF it has missing digit AT LAST OR END (ex 1:2:3:4:5:6:7:)
IF ((i == 0 || i == arrMaybeIPv6.LENGTH - 1) && IsEmptyDigitGroup(arrMaybeIPv6[i]) && expectedDigitGroupsLength != 9)
{
RETURN FALSE;
}
//IF it has missing digits (ex 1:2:3:4:5:6)
IF (i == arrMaybeIPv6.LENGTH - 1 && numberOfEmptyDigitGroups == 0 && arrMaybeIPv6.LENGTH < 8)
{
RETURN FALSE;
}
}
RETURN TRUE;
}
bool IsIPv4(string lastDigitGroup)
{
//IF lastDigitGroup has special CHAR, THEN get the FIRST GROUP FOR IPV4 validation (ex ::123.12.2.1/60)
string maybeIPv4 = lastDigitGroup.Split('/','%')[0];
Match match = Regex.Match(maybeIPv4, IPv4Validation);
RETURN match.Success;
}
bool IsIPv6Char(string strChar)
{
Match match = Regex.Match(strChar, charValidator);
RETURN match.Success;
}
bool IsSpecialChar(CHAR ch)
{
IF (ch == '%' || ch == '/')
{
RETURN TRUE;
}
RETURN FALSE;
}
bool HasSpecialCharInIPv6(string lastDigitGroup, bool isPreviousDigitGroupEmpty)
{
FOR (int i = 0; i < lastDigitGroup.LENGTH; i++)
{
//IF cannot find ANY special CHAR AT FIRST 5 chars THEN leave the FOR LOOP
IF (i == 5)
break;
//IF the FIRST digit IS special CHAR, CHECK the previous digits TO be sure it IS a valid IPv6 (ex FE80::/10)
IF (i == 0 && IsSpecialChar(lastDigitGroup[i]) && isPreviousDigitGroupEmpty)
RETURN TRUE;
IF (i != 0 && IsSpecialChar(lastDigitGroup[i]))
RETURN TRUE;
IF (!IsIPv6Char(lastDigitGroup[i].ToString()))
RETURN FALSE;
}
RETURN FALSE;
}
bool IsEmptyDigitGroup(string digitGroup)
{
IF (digitGroup.TRIM() == string.Empty)
RETURN TRUE;
RETURN FALSE;
}
}
我还添加了其他方法,例如如何在文本或文件中搜索IPv6。您可以检查:匹配有效IPv6地址的正则表达式
编辑摘要:映射的ipv4和特殊字符已被覆盖,例如" :: 123.23.23.23"," fe80 :: 3%eth0"," :: ffff:192.1.56.10/96"。
::是有效的IPv6地址(全零地址),那么为什么不接受它呢?
而且,如果您不想接受使用IPv4表示法写入的最后32位的IPv6地址(为什么不接受,它们是有效的地址表示形式),则只需撤销处理它们的regex的最后一部分(以)。
无论如何,正则表达式确实在IPv4注释部分中包含一些错误。 IPv4表示法是写入IPv6地址的后32位的另一种方式,而regex不能处理该地址的所有有效变体。此外,它甚至忘记转义.,因此它还将接受许多无效的字符串。
关于::,我没想到,很酷,我会接受的。关于另一个,我确实想接受ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:192.168.158.190的说法,该说法无效。 regex101.com/r/mO4hJ2这是我用于IPv4 (((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?))的RegExp。我不确定如何添加它来解决您提到的IPv4问题。
我不明白,那么您为什么要问"我需要一个RegEx用于IPv6,而不将其与IPv4结合使用"?
如果确实要接受IPv4地址表示法,则需要更改正则表达式的最后一部分,以使其接受以IPv4表示法结尾的双冒号(正则表达式的第一部分)的每个可能位置。我建议使用现有的地址解析例程,而不是尝试将所有内容放入一个复杂的(因此容易出错的)正则表达式中。
我的意思是"我需要一个RegEx来与IPv4结合使用而不将其与IPv4结合使用"是:在我链接到的另一篇文章中,它们都是验证单个IPv4值(例如255.255.255.0)或IPv6值(例如, ABCD:ABCD:ABCD:ABCD:ABCD:ABCD)。我只需要IPv6,但应允许使用IPv4映射的IPv6格式(45字节):ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:192.168.158.190(stackoverflow.com/a/7477384/3112803)。我似乎可以开始使用后者。 regex101.com/r/dG0cN9谢谢,我会继续愚弄它。
在@nhahtdh的大量帮助下,我找到了将其分解为最佳解决方案的方法,该回答为https://stackoverflow.com/a/21943960/3112803。以下是在PL/SQL中如何执行此操作的示例,但是可以在其他语言中以这种方式执行。我将在ColdFusion中执行相同的操作。对于PL/SQL,该模式需要保持在512个字符以内,因此将其分解很有效,而且很容易理解。它通过了我在原始问题中的所有测试用例。
IF (
/* IPv6 expanded */
REGEXP_LIKE(v, '\A[[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){7}\z')
/* IPv6 shorthand */
OR (NOT REGEXP_LIKE(v, '\A(.*?[[:xdigit:]](:|\z)){8}')
AND REGEXP_LIKE(v, '\A([[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){0,6})?::([[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){0,6})?\z'))
/* IPv6 dotted-quad notation, expanded */
OR REGEXP_LIKE(v, '\A[[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){5}:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z')
/* IPv6 dotted-quad notation, shorthand */
OR (NOT REGEXP_LIKE(v, '\A(.*?[[:xdigit:]]:){6}')
AND REGEXP_LIKE(v, '\A([[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){0,4})?::([[:xdigit:]]{1,4}:){0,5}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z'))
) THEN