比较版本号字符串(主版本号、次版本号、修订版本号、测试版号)。

15
我有一个与设备固件通信的应用程序。随着固件的变化,它被版本化为格式为{major}.{minor}.{revision}[beta[{beta}]]。举个例子,当前版本是0.4.7beta,接下来会是0.4.7beta2,偶尔还有0.4.7,再然后是0.4.8beta。不幸的是,固件的版本控制格式不在我掌控之中,因此我无法更改它。
我需要一种比较固件之间的方式。基本上,我需要一个函数。
boolean isFirmwareNewer(String testFW, String baseFW);
我所做的是将这种格式转换为简单的 int 。因此,0.4.7beta2 将变成 00040702 (每个级别为2个数字)。问题在于:
  1. 我的代码难以阅读(> 40行和3个方法)
  2. 我相信,有一种优雅的解决方案(也许使用正则表达式?)
  3. 我想要一个通配符 0.0.0 ,它根据定义更新
  4. 这会处理错误的 beta 版本(0.4.7beta2 不比0.4.7更新)。这很容易考虑到( if(testFW.contains(“beta”))testFWValue -= 100; ),但这也不是特别优雅的方法。

你们通常如何处理这个问题(或者你们会怎么做)?

如果需要,我可以附上我目前正在使用的代码,但是如我所说,它超过了40行代码且不易阅读(这就是我正在寻找更好的方式的原因)。


1
0.4.70.4.7beta2更新,对吗? - aioobe
@aioobe 哎呀,谢谢提醒,我会在帖子中进行更正的。 ;) - brimborium
3个回答

24

以下是一个建议:

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;
}
它使用一个小技巧,将beta部分翻译为以下内容:
  • ""(没有beta后缀)→ Beta MAX_INT
  • "beta" → Beta 1(因为它在"beta2"之前)
  • "betaX" → Beta X
请注意,如果两个版本相同,则返回true

我喜欢这种方法。它比我的当前版本更加简洁,并且比我更优雅地处理了beta相关的问题。谢谢!(如果完全相同,则返回true是可以接受的,因为这就是我想要的) - brimborium
1
...哦,对了。你可能想要添加 if (testFW.equals("0.0.0")) return true - aioobe
1
已经完成了。;)但那是一个特殊情况,如果你不包含它在你的答案中,那也没关系。 - brimborium

10

我会实现一个可比较的类:

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)));
    }


}

然后以标准Java方式进行比较:

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

1
谢谢您的回答。我认为这对我的需求有点过度,但这将是一个干净的解决方案。+1 - brimborium
1
只是一个小建议,使用Integer.compare而不是相减。这种解决方案更加友好,且稍微更易读。 - aioobe
尤其是因为您在同一段代码片段中实际提到了 Integer.MAX_VALUE ;-) - aioobe

3

我在我的项目中采用了以下方法,遵循语义化版本控制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;
}

请使用versionCompare(String str1, String str2)方法进行任何比较。 代码示例:---
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);
    }
}

上面示例的输出如下:---
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接