Comparing version number strings (major, minor, revision, beta)

北慕城南 提交于 2019-11-30 02:48:45

问题


I have an application that communicates with the firmware of a device. As there are changes to the firmware, it is versioned with the format {major}.{minor}.{revision}[beta[{beta}]]. To give you a few examples, the current version is 0.4.7beta which will be followed by 0.4.7beta2 and occasionally 0.4.7, followed by 0.4.8beta. The versioning format of the firmware is unfortunately not under my control, so I can not change it.

I need a way of comparing firmwares with each other. Basically, I need a function

boolean isFirmwareNewer(String testFW, String baseFW);

What I did so far was to convert this format to a simple int. So 0.4.7beta2 will become 00040702 (2 digits for each level). The problem is, that

  1. My code is difficult to read (>40 lines and 3 methods)
  2. I am sure, there is a elegant solution to this (maybe using regex?)
  3. I want to have a wildcard 0.0.0 which is newer by definition
  4. This handles the beta versions incorrect (0.4.7beta2 is not newer than 0.4.7). That is easy to account for (if (testFW.contains("beta")) testFWValue -= 100;, but it's not really elegant as well.

How do you guys do this normally (or how would you do it)?

If you want, I can attach the code I am currently working with, but as I said, it's >40 lines of code and not really readable (which is the reason why I am looking for a better way to do this).


回答1:


Here's one suggestion:

static int[] getVersionNumbers(String ver) {
    Matcher m = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(beta(\\d*))?")
                       .matcher(ver);
    if (!m.matches())
        throw new IllegalArgumentException("Malformed FW version");

    return new int[] { Integer.parseInt(m.group(1)),  // major
            Integer.parseInt(m.group(2)),             // minor
            Integer.parseInt(m.group(3)),             // rev.
            m.group(4) == null ? Integer.MAX_VALUE    // no beta suffix
                    : m.group(5).isEmpty() ? 1        // "beta"
                    : Integer.parseInt(m.group(5))    // "beta3"
    };
}

static boolean isFirmwareNewer(String testFW, String baseFW) {

    int[] testVer = getVersionNumbers(testFW);
    int[] baseVer = getVersionNumbers(baseFW);

    for (int i = 0; i < testVer.length; i++)
        if (testVer[i] != baseVer[i])
            return testVer[i] > baseVer[i];

    return true;
}

It uses a little trick and translates the beta-part as follows:

  • "" (no beta suffix) → Beta MAX_INT
  • "beta" → Beta 1 (since it preceeds "beta2")
  • "betaX" → Beta X

Note that it return true if both versions are identical.




回答2:


I would implement a comparable class:

class Version implements Comparable<Version> {
    int major;
    int minor;
    int rev;
    int beta = Integer.MAX_VALUE;


    public int compareTo(Version o) {
        if (this.major != o.major) {
            return Integer.compare(this.major, o.major);
        }
        if (this.minor != o.minor) {
            return Integer.compare(this.minor, o.minor);
        }
        if (this.rev != o.rev) {
            return Integer.compare(this.rev, o.rev);
        }
        if (this.beta != o.beta) {
            return Integer.compare(this.beta, o.beta);
        }
        return 0;
    }


    public static Version parse(String version) {
        // TODO: implement parsing here
        // 1.1.1      - beta = MAX_VALUE
        // 1.1.1beta  - beta = 1
        // 1.1.1beta2 - beta = 2
        return new Version();
    }


    @Override
    public String toString() {
        return "" + major + "." + minor + "." + rev
                + (beta == Integer.MAX_VALUE ? "" 
                        : (beta == 1 ? "beta" : 
                            ("beta" + beta)));
    }


}

Then compare in a standard java way:

if (Version.parse(testFW).compareTo(Version.parse(baseFW)) < 0) {
   // Version is newer!
}



回答3:


For my project I have used this approach by following Semantic Versioning 2.0.0:

private static String[] formatVersionString(String[] strArr){
    //remove trailing 0s
    List<String> list = new ArrayList<>();
    boolean foundChar = false;
    for(int i=strArr.length-1;i>=0;i--){
        String curChar = strArr[i];
        if(curChar.equals("0") && !foundChar){
            continue;
        } else{
            list.add(strArr[i]);
            foundChar = true;
        }

    }
    Collections.reverse(list);
    return list.toArray(new String[list.size()]);
}

private static String getPreReleaseBuildStr(String buildStr){
    //removing build metadata
    if(buildStr == null){
        return null;
    }
    String [] a = buildStr.split("\\+");
    if(a.length>0){
        return a[0];
    } else{
        return null;
    }
}

private static int compareVersionString(String str1,String str2){
    int ret = 0;
    String[] verStr1 = formatVersionString(str1.split("\\."));
    String[] verStr2 = formatVersionString(str2.split("\\."));

    int i = 0;
    // set index to first non-equal ordinal or length of shortest version string
    while (i < verStr1.length && i < verStr2.length && verStr1[i].equals(verStr2[i])) {
        i++;
    }

    // compare first non-equal ordinal number
    if (i < verStr1.length && i < verStr2.length) {
        int diff = 0;
        try{
            if(verStr1[i] == null || verStr1[i].trim().length() == 0) {
                verStr1[i] = "0";
            }
            if(verStr2[i] == null || verStr2[i].trim().length() == 0) {
                verStr2[i] = "0";
            }
            diff = Integer.valueOf(verStr1[i]).compareTo(Integer.valueOf(verStr2[i]));
        }catch(NumberFormatException e){
            diff = verStr1[i].compareTo(verStr2[i]);
        } finally{
            ret = Integer.signum(diff);
        }
    } else{
        // the strings are equal or one string is a substring of the other
        // e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4"

        ret = Integer.signum(verStr1.length - verStr2.length);
    }

    return ret;
}

/**
 * Compares two version strings.
 * follow this link for more info http://semver.org/
 *
 * Use this instead of String.compareTo() for a non-lexicographical
 * comparison that works for version strings. e.g. "1.10".compareTo("1.6").
 *
 * Ex:--
 * //1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0 < 2.0.0.6
 *
 * @param str1 a string of ordinal numbers separated by decimal points.
 * @param str2 a string of ordinal numbers separated by decimal points.
 * @return The result is a negative integer if str1 is _numerically_ less than str2.
 *         The result is a positive integer if str1 is _numerically_ greater than str2.
 *         The result is zero if the strings are _numerically_ equal.
 */

public static int versionCompare(String str1, String str2) {

    int ret = 0;
    String[] val1 = str1.split("-");
    String[] val2 = str2.split("-");

    String preReleaseVer1 = null, preReleaseVer2 = null;
    if(val1.length>1){
        preReleaseVer1 = getPreReleaseBuildStr(val1[1]);
    }
    if(val2.length>1){
        preReleaseVer2 = getPreReleaseBuildStr(val2[1]);
    }

    ret = compareVersionString(val1[0],val2[0]);

    if(ret == 0){
        //if both version are equal then compare with pre_release String
        if(preReleaseVer1 == null && preReleaseVer2 == null){
              ret = 0;
        } else if(preReleaseVer1 == null && preReleaseVer2!=null){
            //1.0.0 > 1.0.0-beta
              ret = 1;
        } else if(preReleaseVer1 != null && preReleaseVer2==null){
            //1.0.0-beta < 1.0.0
            ret = -1;
        } else{
            //both hasve pre release string
            ret = compareVersionString(preReleaseVer1,preReleaseVer2);
        }
    }

    return ret;
}

Use versionCompare(String str1, String str2) method for any comparison

Code Example:---

public class Main
{
    public static void main (String[] args)
    {
        //1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0

        Test(new String[]{"1.0.0-alpha","1.0.0-alpha.1","1.0.0-alpha.beta","1.0.0-beta",
        "1.0.0-beta.2","1.0.0-beta.11","1.0.0-rc.1","1.0.0","2.0.0.1"});  
    }
    private static void Test(String[] versions) {
        for (int i = 0; i < versions.length; i++) {
            for (int j = 0; j < versions.length; j++) {
            Test(versions[i], versions[j]);
            }
        }
    }

    private static void Test(String v1, String v2) {
        int result = versionCompare(v1,v2);
        String op = "==";
        if (result < 0) op = "<";
        if (result > 0) op = ">";
        System.out.printf("%s %s %s\n", v1, op, v2);
    }
}

The Output of the above example is given below:---

1.0.0-alpha == 1.0.0-alpha
1.0.0-alpha < 1.0.0-alpha.1
1.0.0-alpha < 1.0.0-alpha.beta
1.0.0-alpha < 1.0.0-beta
1.0.0-alpha < 1.0.0-beta.2
1.0.0-alpha < 1.0.0-beta.11
1.0.0-alpha < 1.0.0-rc.1
1.0.0-alpha < 1.0.0
1.0.0-alpha < 2.0.0.1
1.0.0-alpha.1 > 1.0.0-alpha
1.0.0-alpha.1 == 1.0.0-alpha.1
1.0.0-alpha.1 < 1.0.0-alpha.beta
1.0.0-alpha.1 < 1.0.0-beta
1.0.0-alpha.1 < 1.0.0-beta.2
1.0.0-alpha.1 < 1.0.0-beta.11
1.0.0-alpha.1 < 1.0.0-rc.1
1.0.0-alpha.1 < 1.0.0
1.0.0-alpha.1 < 2.0.0.1
1.0.0-alpha.beta > 1.0.0-alpha
1.0.0-alpha.beta > 1.0.0-alpha.1
1.0.0-alpha.beta == 1.0.0-alpha.beta
1.0.0-alpha.beta < 1.0.0-beta
1.0.0-alpha.beta < 1.0.0-beta.2
1.0.0-alpha.beta < 1.0.0-beta.11
1.0.0-alpha.beta < 1.0.0-rc.1
1.0.0-alpha.beta < 1.0.0
1.0.0-alpha.beta < 2.0.0.1
1.0.0-beta > 1.0.0-alpha
1.0.0-beta > 1.0.0-alpha.1
1.0.0-beta > 1.0.0-alpha.beta
1.0.0-beta == 1.0.0-beta
1.0.0-beta < 1.0.0-beta.2
1.0.0-beta < 1.0.0-beta.11
1.0.0-beta < 1.0.0-rc.1
1.0.0-beta < 1.0.0
1.0.0-beta < 2.0.0.1
1.0.0-beta.2 > 1.0.0-alpha
1.0.0-beta.2 > 1.0.0-alpha.1
1.0.0-beta.2 > 1.0.0-alpha.beta
1.0.0-beta.2 > 1.0.0-beta
1.0.0-beta.2 == 1.0.0-beta.2
1.0.0-beta.2 < 1.0.0-beta.11
1.0.0-beta.2 < 1.0.0-rc.1
1.0.0-beta.2 < 1.0.0
1.0.0-beta.2 < 2.0.0.1
1.0.0-beta.11 > 1.0.0-alpha
1.0.0-beta.11 > 1.0.0-alpha.1
1.0.0-beta.11 > 1.0.0-alpha.beta
1.0.0-beta.11 > 1.0.0-beta
1.0.0-beta.11 > 1.0.0-beta.2
1.0.0-beta.11 == 1.0.0-beta.11
1.0.0-beta.11 < 1.0.0-rc.1
1.0.0-beta.11 < 1.0.0
1.0.0-beta.11 < 2.0.0.1
1.0.0-rc.1 > 1.0.0-alpha
1.0.0-rc.1 > 1.0.0-alpha.1
1.0.0-rc.1 > 1.0.0-alpha.beta
1.0.0-rc.1 > 1.0.0-beta
1.0.0-rc.1 > 1.0.0-beta.2
1.0.0-rc.1 > 1.0.0-beta.11
1.0.0-rc.1 == 1.0.0-rc.1
1.0.0-rc.1 < 1.0.0
1.0.0-rc.1 < 2.0.0.1
1.0.0 > 1.0.0-alpha
1.0.0 > 1.0.0-alpha.1
1.0.0 > 1.0.0-alpha.beta
1.0.0 > 1.0.0-beta
1.0.0 > 1.0.0-beta.2
1.0.0 > 1.0.0-beta.11
1.0.0 > 1.0.0-rc.1
1.0.0 == 1.0.0
1.0.0 < 2.0.0.1
2.0.0.1 > 1.0.0-alpha
2.0.0.1 > 1.0.0-alpha.1
2.0.0.1 > 1.0.0-alpha.beta
2.0.0.1 > 1.0.0-beta
2.0.0.1 > 1.0.0-beta.2
2.0.0.1 > 1.0.0-beta.11
2.0.0.1 > 1.0.0-rc.1
2.0.0.1 > 1.0.0
2.0.0.1 == 2.0.0.1
3.5.6-beta == 3.5.6-beta
3.5.6-beta == 3.5.6-beta.0
3.5.6-beta.0 == 3.5.6-beta
3.5.6-beta.0 == 3.5.6-beta.0


来源:https://stackoverflow.com/questions/11501192/comparing-version-number-strings-major-minor-revision-beta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!