166

How do I compare version numbers?

For instance:

x = 1.23.56.1487.5

y = 1.24.55.487.2

Venkat
  • 2,549
  • 2
  • 28
  • 61
Sankar M
  • 4,549
  • 12
  • 37
  • 55
  • 5
    What do you want the result to be? – BoltClock Sep 27 '11 at 11:12
  • 5
    a version with 5 parts? If you were using the typical 4 part version you could use the System.Version class, which includes methods to compare versions and parse the version string – psousa Sep 27 '11 at 11:14
  • 1
    What are the types of x and y? – Gleno Sep 27 '11 at 11:14
  • 7
    Whenever someone says "Don't use `X`, `Y` or `Z`", it always makes me wonder why. Why do you not want to use the `split` function? The `split` function seems like a good way to do this if you aren't going to use the `System.Version` class. – Bob2Chiv Sep 27 '11 at 11:35

7 Answers7

382

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;

    }
}
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
JohnD
  • 14,327
  • 4
  • 40
  • 53
  • 5
    Only if the version consists from 2-4 parts – username Sep 27 '11 at 11:18
  • @dev_Boston only one exception...just do with these values v1=1.0001 and v2=1.1 .it gives me equal. – Sankar M Sep 27 '11 at 11:31
  • 11
    Yes, the version strings are not decimal strings, and prepending zeroes to a portion of the version number is irrelevant. In other words, "00001" is equal to "1" in the 2nd part of the version string. – JohnD Sep 27 '11 at 11:34
  • 13
    You can compare more readably as `Version.Parse(v1) < Version.Parse(v2)`, because `operator >(Version v1, Version v2)` is implemented. – Andrey Moiseev Jan 09 '17 at 18:34
  • 3
    Be aware that Version.Parse("6.0.0") is less than (<) Version.Parse("6.0.0.0") (i.e. are NOT equal). `Debug.Assert(new Version("6.0.0") < new Version("6.0.0.0"));` – adospace Nov 05 '18 at 14:54
14

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.

Andreas
  • 6,447
  • 2
  • 34
  • 46
7

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; 
    }
}
too
  • 3,009
  • 4
  • 37
  • 51
5
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;
    }
user3790307
  • 67
  • 1
  • 1
2

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());
    }   
}
Jay
  • 173
  • 1
  • 7
1

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

Stef Heyenrath
  • 9,335
  • 12
  • 66
  • 121
Fabian Bigler
  • 10,403
  • 6
  • 47
  • 70
  • To give all digits 3 places, the code should actually be : "version.Major * 1000000000L + version.Minor * 1000000L + version.Build * 1000L + version.Revision" – Stef Heyenrath Oct 26 '18 at 12:18
  • 1
    @StefHeyenrath That is correct, feel free to adjust the above code to your own needs. – Fabian Bigler Oct 26 '18 at 14:50
1

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;
    
    }
Wilmar Arias
  • 186
  • 8