开发者

Compare version numbers without using split function

How do I co开发者_开发技巧mpare version numbers?

For instance:

x = 1.23.56.1487.5

y = 1.24.55.487.2


Can you use the Version class?
https://learn.microsoft.com/en-us/dotnet/api/system.version

It has an IComparable interface. Be aware this won't work with a 5-part version string like you've shown (is that really your version string?). Assuming your inputs are strings, here's a working sample with the normal .NET 4-part version string:

static class Program
{
    static void Main()
    {
        string v1 = "1.23.56.1487";
        string v2 = "1.24.55.487";

        var version1 = new Version(v1);
        var version2 = new Version(v2);

        var result = version1.CompareTo(version2);
        if (result > 0)
            Console.WriteLine("version1 is greater");
        else if (result < 0)
            Console.WriteLine("version2 is greater");
        else
            Console.WriteLine("versions are equal");
        return;

    }
}


If you can live with the major.minor.build.revision scheme you could use the .Net Version class. Otherwise you'd have to implement some kind of parsing from left to right and continuing until you have a difference or return that two versions are equal.


In addition to @JohnD 's answer there might be a need to compare only partial version numbers without using Split('.') or other string <-> int conversion bloat. I've just written an extension method CompareTo with 1 additional argument - number of significant parts of version number to compare (between 1 and 4).

public static class VersionExtensions
{
    public static int CompareTo(this Version version, Version otherVersion, int significantParts)
    {
        if(version == null)
        {
            throw new ArgumentNullException("version");
        }
        if(otherVersion == null)
        {
            return 1;
        }

        if(version.Major != otherVersion.Major && significantParts >= 1)
            if(version.Major > otherVersion.Major)
                return 1;
            else
                return -1;

        if(version.Minor != otherVersion.Minor && significantParts >= 2)
            if(version.Minor > otherVersion.Minor)
                return 1;
            else
                return -1;

        if(version.Build != otherVersion.Build && significantParts >= 3)
            if(version.Build > otherVersion.Build)
                return 1;
            else
                return -1;

        if(version.Revision != otherVersion.Revision && significantParts >= 4)
            if(version.Revision > otherVersion.Revision)
                return 1;
            else
                return -1;

        return 0; 
    }
}


public int compareVersion(string Version1,string Version2)
    {
        System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"([\d]+)");
        System.Text.RegularExpressions.MatchCollection m1 = regex.Matches(Version1);
        System.Text.RegularExpressions.MatchCollection m2 = regex.Matches(Version2);
        int min = Math.Min(m1.Count,m2.Count);
        for(int i=0; i<min;i++)
        {
            if(Convert.ToInt32(m1[i].Value)>Convert.ToInt32(m2[i].Value))
            {
                return 1;
            }
            if(Convert.ToInt32(m1[i].Value)<Convert.ToInt32(m2[i].Value))
            {
                return -1;
            }               
        }
        return 0;
    }


Here's mine. I needed to compare some wacky version strings like "3.2.1.7650.b40" versus "3.10.1" so I couldn't use the VersionInfo object as suggested above. This is quick and dirty so ding me for style. I did also provide a short function to test it.

using System;
                    
public class Program
{
    public static void Main()
    {
        Test_CompareVersionStrings();
    }
    
    /// <summary>
    /// Compare two version strings, e.g.  "3.2.1.0.b40" and "3.10.1.a".
    /// V1 and V2 can have different number of components.
    /// Components must be delimited by dot.
    /// </summary>
    /// <remarks>
    /// This doesn't do any null/empty checks so please don't pass dumb parameters
    /// </remarks>
    /// <param name="v1"></param>
    /// <param name="v2"></param>
    /// <returns>
    /// -1 if v1 is lower version number than v2,
    /// 0 if v1 == v2,
    /// 1 if v1 is higher version number than v2,
    /// -1000 if we couldn't figure it out (something went wrong)
    /// </returns>
    private static int CompareVersionStrings(string v1, string v2)
    {
        int rc = -1000;

        v1 = v1.ToLower();
        v2 = v2.ToLower();

        if (v1 == v2)
            return 0;

        string[] v1parts = v1.Split('.');
        string[] v2parts = v2.Split('.');

        for (int i = 0; i < v1parts.Length; i++)
        {
            if (v2parts.Length < i+1)
                break; // we're done here
            
            string v1Token = v1parts[i];
            string v2Token = v2parts[i];
            
            int x;
            bool v1Numeric = int.TryParse(v1Token, out x);
            bool v2Numeric = int.TryParse(v2Token, out x);
            
            // handle scenario {"2" versus "20"} by prepending zeroes, e.g. it would become {"02" versus "20"}
            if (v1Numeric && v2Numeric) {
                while (v1Token.Length < v2Token.Length)
                    v1Token = "0" + v1Token;
                while (v2Token.Length < v1Token.Length)
                    v2Token = "0" + v2Token;
            }

            rc = String.Compare(v1Token, v2Token, StringComparison.Ordinal);
            //Console.WriteLine("v1Token=" + v1Token + " v2Token=" + v2Token + " rc=" + rc);
            if (rc != 0)
                break;
        }

        if (rc == 0)
        {
            // catch this scenario: v1="1.0.1" v2="1.0"
            if (v1parts.Length > v2parts.Length)
                rc = 1; // v1 is higher version than v2
            // catch this scenario: v1="1.0" v2="1.0.1"
            else if (v2parts.Length > v1parts.Length)
                rc = -1; // v1 is lower version than v2
        }

        if (rc == 0 || rc == -1000)
            return rc;
        else
            return rc < 0 ? -1 : 1;
    }
    
    private static int _CompareVersionStrings(string v1, string v2)
    {
        int rc = CompareVersionStrings(v1, v2);
        Console.WriteLine("Compare v1: " + v1 + "  v2: " + v2 + "  result: " + rc);
        return rc;
    }

    // for debugging
    private static void Test_CompareVersionStrings()
    {
        bool allPass = true;

        // should be equal
        allPass &= (0 == _CompareVersionStrings("1", "1"));
        allPass &= (0 == _CompareVersionStrings("1.1", "1.1"));
        allPass &= (0 == _CompareVersionStrings("3.3.a20", "3.3.A20"));

        // v1 should be lower
        allPass &= (-1 == _CompareVersionStrings("1", "2"));
        allPass &= (-1 == _CompareVersionStrings("1.0", "1.0.1"));
        allPass &= (-1 == _CompareVersionStrings("1.0", "1.1"));
        allPass &= (-1 == _CompareVersionStrings("1.0.0.3", "1.1"));
        allPass &= (-1 == _CompareVersionStrings("1.2.3.4", "1.2.3.4b"));
        allPass &= (-1 == _CompareVersionStrings("1.2.3.4", "1.2.3.4.b"));
        allPass &= (-1 == _CompareVersionStrings("1.8.0", "20.0.0.0"));
        allPass &= (-1 == _CompareVersionStrings("5.6.0.788.2", "20.0.0.0"));

        // v1 should be higher
        allPass &= (1 == _CompareVersionStrings("2", "1"));
        allPass &= (1 == _CompareVersionStrings("1.0.1", "1.0"));
        allPass &= (1 == _CompareVersionStrings("1.1", "1.0"));
        allPass &= (1 == _CompareVersionStrings("1.1", "1.0.0.3"));
        allPass &= (1 == _CompareVersionStrings("1.2.3.4b", "1.2.3.4"));
        allPass &= (1 == _CompareVersionStrings("1.2.3.4.b", "1.2.3.4")); 
        allPass &= (1 == _CompareVersionStrings("20.0.0.0", "5.6.0.788.2"));

        Console.WriteLine("allPass = " + allPass.ToString());
    }   
}


If for some reason you are not allowed to use the compare-method of the Version directly (e.g. in a client-server scenario), another approach is to extract a long number from the version and then compare the numbers with each other. However, the number needs to have the following format: Two digits for Major, Minor and Revision and four for Build.

How to extract the version number:

var version = Assembly.GetExecutingAssembly().GetName().Version;

long newVersion = version.Major * 1000000000L + 
                   version.Minor * 1000000L + 
                   version.Build * 1000L + 
                   version.Revision;

And then somewhere else you can just compare:

if(newVersion > installedVersion)
{
  //update code
}

Note: the installedVersion is a previously extracted long number


I found this algoritm on internet seems to work well.

//https://www.geeksforgeeks.org/compare-two-version-numbers/amp/

static int versionCompare(string v1, string v2)
    {
        // vnum stores each numeric
    
        // part of version
    
        int vnum1 = 0, vnum2 = 0;
    
        // loop until both string are
        // processed
    
        for (int i = 0, j = 0; (i < v1.Length || j < v2.Length);)
    
        {
            // storing numeric part of
            // version 1 in vnum1
            while (i < v1.Length && v1[i] != '.')
            {
    
                vnum1 = vnum1 * 10 + (v1[i] - '0');
    
                i++;
            }
            // storing numeric part of
    
            // version 2 in vnum2
    
            while (j < v2.Length && v2[j] != '.')
            {
                vnum2 = vnum2 * 10 + (v2[j] - '0');
                j++;
            }
            if (vnum1 > vnum2)
                return 1;
    
            if (vnum2 > vnum1)
                return -1;
    
            // if equal, reset variables and
    
            // go for next numeric part
            vnum1 = vnum2 = 0;
            i++;
            j++;
        }
    
        return 0;
    
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜