C# IPv6缩写(零块压缩)逻辑。我用的是c

C# IPv6缩写(零块压缩)逻辑。我用的是c,c#,ip,logic,ipv6,abbreviation,C#,Ip,Logic,Ipv6,Abbreviation,这是完整的未压缩IP地址2001:0008:0000:CD30:0000:0000:0000:0000:0101 我需要像这样压缩它 2001:8:0:CD30::101 但我只能像这样压缩块中的零 2001:8:0:CD30:0:0:0:101 使用此代码 string output = ""; string a = textBox1.Text; if (a.Length != 39 ) MessageBox.Show("Invali

这是完整的未压缩IP地址2001:0008:0000:CD30:0000:0000:0000:0000:0101 我需要像这样压缩它 2001:8:0:CD30::101 但我只能像这样压缩块中的零 2001:8:0:CD30:0:0:0:101 使用此代码

 string output = "";
        string a = textBox1.Text;
        if (a.Length != 39  )
            MessageBox.Show("Invalid IP please enter the IPv6 IP in this format 6cd9:a87a:ad46:0005:ad40:0000:5698:8ab8");
        else
        {
            for (int i = 0; i < a.Length; i++)
            {
                if ((a[i] >= '1' && a[i] <= '9') || (Char.ToLower(a[i]) >= 'a' && Char.ToLower(a[i]) <= 'f') || ((i + 1) % 5 == 0 && a[i] == ':'))
                {
                    output = output + a[i];
                }
                else if ((a[i]=='0' && a[i-1]==':') || (a[i]=='0' && a[i-1]=='0' && a[i-2]==':') || (a[i]=='0' && a[i-1]=='0' && a[i-2]=='0' && a[i-3]==':')) 
                {

                }
                else if (a[i] == '0')
                {
                    output = output + a[i];
                }

                else
                {
                    MessageBox.Show("Invalid IP please enter the IPv6 IP in this format 6cd9:a87a:ad46:0005:ad40:0000:5698:8ab8");
                }
            }

            textBox2.Text = output;
        }

我在使用c,但我只需要关于如何删除整块零的编程逻辑问题是ip中可能有超过1组包含所有零的块,但只有一个块应该缩写。

比我预期的要复杂得多,但这里有了使用正则表达式的方法:

        private static string Compress(string ip)
        {
            var removedExtraZeros = ip.Replace("0000","*");

            //2001:0008:*:CD30:*:*:*:0101
            var blocks = ip.Split(':');

            var regex = new Regex(":0+");
            removedExtraZeros = regex.Replace(removedExtraZeros, ":");


            //2001:8:*:CD30:*:*:*:101

            var regex2 = new Regex(":\\*:\\*(:\\*)+:");
            removedExtraZeros = regex2.Replace(removedExtraZeros, "::");
            //2001:8:*:CD30::101

            return removedExtraZeros.Replace("*", "0");
        }

这比我想象的要复杂得多,但这里有了使用正则表达式的方法:

        private static string Compress(string ip)
        {
            var removedExtraZeros = ip.Replace("0000","*");

            //2001:0008:*:CD30:*:*:*:0101
            var blocks = ip.Split(':');

            var regex = new Regex(":0+");
            removedExtraZeros = regex.Replace(removedExtraZeros, ":");


            //2001:8:*:CD30:*:*:*:101

            var regex2 = new Regex(":\\*:\\*(:\\*)+:");
            removedExtraZeros = regex2.Replace(removedExtraZeros, "::");
            //2001:8:*:CD30::101

            return removedExtraZeros.Replace("*", "0");
        }

如果希望在不使用正则表达式的情况下获得相同的结果:

没有在stackalloc、spans等微观优化上花费时间,但这给出了一个想法。有关优化的参考,您可以查看.net内核中IPAddress.Parse的实现。请注意,IPAddress.Parse的结果将给出与上述示例不同的结果:

Compress        -> 2001:8:0:CD30:0:0:0:101 
IPAddress.Parse -> 2001:8:0:cd30::101
这可以通过进入一些对象和其他想法来解决

编辑:

在与我的一位同事聊天后,我花了一些时间编写了一个优化版本。我还没有花时间清理代码,所以将来的编辑可能会更干净

public string Compress(string value)
{
    Span<char> chars = stackalloc char[value.Length];
    const char zero = '0';
    const char colon = ':';
    int index = 0;
    int positionInSegment = 0;
    bool startsWithZero;

    while (index < _originalValue.Length)
    {
        startsWithZero = value[index] == zero && positionInSegment == 0;
        positionInSegment++;
        if (startsWithZero)
        {
            if (index == value.Length - 1)
            {
                chars[index] = zero;
                break;
            }
                        
            if (value[index + 1] == colon)
            {
                chars[index] = zero;
                positionInSegment = 0;
                index++;
                continue;
            }
                        
            positionInSegment = 0;
            index++;
            continue;
        }

        if (value[index] == colon)
        {
            positionInSegment = 0;
            chars[index] = colon;
            index++;
            continue;
        }
                    
        chars[index] = value[index];
        index++;
    }

    return chars.ToString();
}
我还为将来的参考创建了一个公共要点:

如果希望在不使用正则表达式的情况下获得相同的结果:

没有在stackalloc、spans等微观优化上花费时间,但这给出了一个想法。有关优化的参考,您可以查看.net内核中IPAddress.Parse的实现。请注意,IPAddress.Parse的结果将给出与上述示例不同的结果:

Compress        -> 2001:8:0:CD30:0:0:0:101 
IPAddress.Parse -> 2001:8:0:cd30::101
这可以通过进入一些对象和其他想法来解决

编辑:

在与我的一位同事聊天后,我花了一些时间编写了一个优化版本。我还没有花时间清理代码,所以将来的编辑可能会更干净

public string Compress(string value)
{
    Span<char> chars = stackalloc char[value.Length];
    const char zero = '0';
    const char colon = ':';
    int index = 0;
    int positionInSegment = 0;
    bool startsWithZero;

    while (index < _originalValue.Length)
    {
        startsWithZero = value[index] == zero && positionInSegment == 0;
        positionInSegment++;
        if (startsWithZero)
        {
            if (index == value.Length - 1)
            {
                chars[index] = zero;
                break;
            }
                        
            if (value[index + 1] == colon)
            {
                chars[index] = zero;
                positionInSegment = 0;
                index++;
                continue;
            }
                        
            positionInSegment = 0;
            index++;
            continue;
        }

        if (value[index] == colon)
        {
            positionInSegment = 0;
            chars[index] = colon;
            index++;
            continue;
        }
                    
        chars[index] = value[index];
        index++;
    }

    return chars.ToString();
}
我还为将来的参考创建了一个公共要点:

您只需删除额外的0,对吗?是的,但问题是,如果IP是这样的2001:0000:0000:CD30:0000:0000:0000:a141,那么它将缩写为这样的2001:0:0:CD30::a141,nt是这样的2001::CD30:0:0:a141,这意味着在IP中,整个零块只能压缩为两个冒号一次,并且必须是从以下位置开始的:零块的数量更多,如上例中所示。3个零块被压缩,而其他块没有。好的,所以问题是最后一个不遵循rule@Balder,请参阅RFC 5952,第4.2.3节:,如果在a::,的位置上有其他选择,必须缩短连续16位0字段的最长运行时间,即在2001:0:0:1:0:0:0:1中缩短具有三个连续零字段的序列。当连续16位0字段的长度相等时,即2001:db8:0:0:1:0:0:1,必须缩短第一个零位序列。例如2001:db8::1:0:0:1是正确的表示形式。您只需要删除额外的0,对吗?是的,但问题是如果IP是这样的2001:0000:0000:CD30:0000:0000:a141,那么它将缩写为这样的2001:0:0:CD30::a141,nt是这样的2001::CD30:0:0:a141,这意味着在IP中,整个零块只能压缩到两个冒号它必须是从零的连续块在数量上更多的地方开始的,如上面的示例3中,压缩了零的块,而其他块则没有。好的,所以问题是最后一个不遵循rule@Balder,请参阅RFC 5952,第4.2.3节:,如果在a::,的位置上有其他选择,必须缩短连续16位0字段的最长运行时间,即在2001:0:0:1:0:0:0:1中缩短具有三个连续零字段的序列。当连续16位0字段的长度相等时,即2001:db8:0:0:1:0:0:1,必须缩短第一个零位序列。例如,2001:db8::1:0:0:1是正确的表示形式。非常感谢它的工作和它的出色之处,但请您解释一下这行var regex2=newregex:\*:\*:\*:\*+:;它实际上在做什么?您好,正如您所看到的,我在第一步中将压缩的零替换为,在第二步中,从块中删除左0不会造成混乱。在第三步中,我只查找blokcs,正则表达式的意思是,我要查找以::*开头的块,至少有一个或多个:*块,并以a结尾:我没有选择非常明智的方法将0块替换为,因为*是regexp元字符,当使用元字符时,必须用\,但要理解字符串,必须使用\\METACHAR。若我使用o而不是regex2,那个么它就简单到:newregex:o:o:o+:;非常感谢你的作品,非常棒,但是你能解释一下这行代码吗=
新正则表达式:\*:\*:\*+:;它实际上在做什么?您好,正如您所看到的,我在第一步中将压缩的零替换为,在第二步中,从块中删除左0不会造成混乱。在第三步中,我只查找blokcs,正则表达式的意思是,我要查找以::*开头的块,至少有一个或多个:*块,并以a结尾:我没有选择非常明智的方法将0块替换为,因为*是regexp元字符,当使用元字符时,必须用\,但要理解字符串,必须使用\\METACHAR。若我使用o而不是regex2,那个么它就简单到:newregex:o:o:o+:;