【问题标题】:Regular Expression (RegEx) for IPv6 Separate from IPv4IPv6 的正则表达式 (RegEx) 与 IPv4 分开
【发布时间】:2014-03-05 02:18:40
【问题描述】:

请在标记为重复之前阅读

我无法创建或找到适用于所有 IPv6 格式的 RegEx(我的测试用例如下)。我知道每个人都指向的这个问题:Regular expression that matches valid IPv6 addresses 但是,它们都将 IPv6 与 IPv4 结合起来和/或不适用于我的所有测试用例。

要求:

  1. 我不希望它也验证 IPv4 值,我已经有一个单独的 IPv4 验证函数。
  2. 我需要一个适用于Coldfusion 的模式和一个适用于PL/SQL 的模式。
  3. 因为我在PL/SQL 中使用它,所以它的模式必须保持在512 个字符以下。而Oracle 只支持RegExp 语言的一小部分。因此,ColdFusion 模式最终可能与 PL/SQL 模式不同,这很好,只要它们都有效。
  4. 最终结果不是一个很长的正则表达式,可以拆分。

这是我正在尝试的最新模式:

^(?>(?>([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中不起作用的项:

  1. 匹配
  2. 匹配
  3. 匹配
  4. 匹配
  5. 匹配
  6. ma​​tch(但@Michael Hampton 说这不应该匹配,因为它不是有效的 IPv6 地址,但其他人告诉我它是有效的,所以我不确定这个测试用例。)
  7. 匹配(:: 实际上是一种有效的格式,感谢@Sander Steffann。)
  8. 匹配
  9. 不匹配
  10. 匹配
  11. 不匹配
  12. 不匹配
  13. 不匹配
  14. 匹配
  15. 匹配
  16. 不匹配
  17. 不匹配
  18. 不匹配
  19. 不匹配

我从:http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzai2%2Frzai2ipv6addrformat.htm 获得了测试用例 8-11 并被告知:测试 9 和 11 用于 IPv6 地址前缀,而不是 IPv6 地址,因此不应匹配。

最终结果,我需要他们在这样的语句中工作:

冷融合:

<cfset IndexOfOccurrence1=REFind("^(?>(?>([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 & ::ffff:192.1.56.10/96(接下来的两种格式用于隧道。它允许 IPv6 节点跨 IPv4 基础设施进行通信)0:0:0:0:0:0:192.1.56.10 & ::192.1.56.10/96(来自:publib.boulder.ibm.com/infocenter/iseries/v5r3/…
  • 您在最终评论中给出的最后两个在几年前曾经有效,但 are no longer in use today。您链接的文档已有十多年的历史...
  • 不要使用正则表达式进行 IPv6 解析。这是一场噩梦。

标签: regex oracle coldfusion plsql ipv6


【解决方案1】:

在@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

【讨论】:

    【解决方案2】:

    据我研究,没有适用于所有 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。您可以查看:Regular expression that matches valid IPv6 addresses

    编辑摘要:Ipv4 映射字符和特殊字符已被覆盖,例如“::123.23.23.23”、“fe80::3%eth0”、“::ffff:192.1.56.10/96” .

    【讨论】:

      【解决方案3】:

      :: 是一个有效的 IPv6 地址(全零地址),为什么不接受呢?

      如果您不想接受最后 32 位以 IPv4 表示法编写的 IPv6 地址(为什么不接受,它们是有效的地址表示形式),那么只需撤销处理它们的正则表达式的最后一部分(以::(ffff 开头)。

      无论如何,正则表达式确实在 IPv4 符号部分包含一些错误。 IPv4 表示法只是写入 IPv6 地址的最后 32 位的另一种方式,并且正则表达式不能处理它的所有有效变体。此外,它甚至忘记转义.,因此它也会接受许多无效字符串。

      【讨论】:

      • 关于::,我没有想到,很酷,我会接受的。关于另一个,我确实想接受ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:192.168.158.190 它说它无效。 regex101.com/r/mO4hJ2 这是我一直用于 IPv4 的 RegExp (((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)) 我不知道如何添加它来解决你提到的 IPv4 问题。
      • 我不明白,那你为什么要问“我需要一个 IPv6 的 RegEx 而不与 IPv4 结合”?
      • 如果您确实想接受 IPv4 地址表示法,那么您需要更改正则表达式的最后一部分,以便它接受带有 IPv4 表示法的双冒号(正则表达式的第一部分)的每个可能位置在末尾。我建议使用现有的地址解析例程,而不是尝试将所有内容放入一个复杂(因此容易出错)的正则表达式中......
      • 我的意思是“我需要一个 IPv6 的 RegEx 而不将它与 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 谢谢,我会继续玩这个的。
      猜你喜欢
      • 1970-01-01
      • 2014-11-02
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 2014-03-23
      • 2011-08-24
      相关资源
      最近更新 更多