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

Java 比较版本号字符串(主要版本、次要版本、修订版、测试版),java,regex,versioning,Java,Regex,Versioning,我有一个与设备固件通信的应用程序。由于固件有更改,因此它的版本控制格式为{major}.{minor}.{revision}[beta[{beta}]]。举几个例子,当前版本是0.4.7beta,后面是0.4.7beta2,偶尔是0.4.7,后面是0.4.8beta。很遗憾,固件的版本控制格式不在我的控制之下,因此我无法更改它 我需要一种比较固件的方法。基本上,我需要一个函数 boolean isFirmwareNewer(String testFW, String baseFW); 到目前为

我有一个与设备固件通信的应用程序。由于固件有更改,因此它的版本控制格式为
{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位)。问题是

  • 我的代码很难阅读(>40行和3种方法)
  • 我相信,有一个优雅的解决方案(可能使用regex?)
  • 我想要一个通配符
    0.0.0
    ,它的定义较新
  • 这将处理不正确的beta版本(
    0.4.7beta2
    不比
    0.4.7
    更新)。这很容易解释(
    如果(testFW.contains(“beta”))testFWValue-=100;
    ,但也不是很优雅
  • 你们通常是怎么做的(或者你们会怎么做)

    如果您愿意,我可以附加当前正在使用的代码,但正如我所说的,它的代码行数超过40行,而且不太可读(这就是我正在寻找更好的方法的原因)。

    这里有一个建议:

    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;
    }
    
    static int[]getVersionNumber(字符串版本){
    匹配器m=Pattern.compile(“(\\d+\\\”(\\d+\\”(\\d+)\”(\\d+)(beta(\\d*)?”)
    .matcher(版本号);
    如果(!m.matches())
    抛出新的IllegalArgumentException(“格式错误的FW版本”);
    返回新的int[]{Integer.parseInt(m.group(1)),//major
    Integer.parseInt(m.group(2)),//次
    Integer.parseInt(m.group(3)),//版本。
    m、 组(4)==null?Integer.MAX_值//无beta后缀
    :m.group(5).isEmpty()?1/“beta”
    :Integer.parseInt(m.group(5))/“beta3”
    };
    }
    静态布尔值isFirmwareNewer(字符串testFW、字符串baseFW){
    int[]testVer=getVersionNumbers(testFW);
    int[]baseVer=getVersionNumber(baseFW);
    for(int i=0;ibaseVer[i];
    返回true;
    }
    
    它使用了一个小技巧,并将beta部分翻译如下:

    • (无测试后缀)→ βMAX_INT
    • “测试版”
      → Beta 1(因为它在“Beta 2”之前)
    • “betaX”
      → βX

    请注意,如果两个版本相同,则返回
    true

    我将实现一个类似的类:

    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)));
        }
    
    
    }
    
    类版本实现了可比较的{
    国际专业;
    int小调;
    国际修订版;
    int beta=整数最大值;
    公共整数比较(o版){
    if(this.major!=o.major){
    返回整数。比较(this.major,o.major);
    }
    if(this.minor!=o.minor){
    返回整数。比较(this.minor,o.minor);
    }
    如果(此版本!=o版本){
    返回整数。比较(this.rev,o.rev);
    }
    如果(this.beta!=o.beta){
    返回整数。比较(this.beta,o.beta);
    }
    返回0;
    }
    公共静态版本解析(字符串版本){
    //TODO:在此处实现解析
    //1.1.1-β=最大值
    //1.1.1贝塔-贝塔=1
    //1.1.1贝塔2-贝塔=2
    返回新版本();
    }
    @凌驾
    公共字符串toString(){
    返回“+大调+”+小调+”+修订版
    +(beta==Integer.MAX_值?“
    :(β=1?“β”:
    (β+β);;
    }
    }
    
    然后以标准java方式进行比较:

    if (Version.parse(testFW).compareTo(Version.parse(baseFW)) < 0) {
       // Version is newer!
    }
    
    if(Version.parse(testFW).compareTo(Version.parse(baseFW))<0){
    //版本更新了!
    }
    
    对于我的项目,我通过以下方式使用了这种方法:

    私有静态字符串[]formatVersionString(字符串[]strArr){
    //删除尾部0
    列表=新的ArrayList();
    布尔foundChar=false;
    对于(int i=strArr.length-1;i>=0;i--){
    字符串curChar=strArr[i];
    if(curChar.equals(“0”)&&!foundChar){
    继续;
    }否则{
    列表。添加(strArr[i]);
    foundChar=true;
    }
    }
    收款。反向(列表);
    return list.toArray(新字符串[list.size()]);
    }
    私有静态字符串getPreReleaseBuildStr(字符串buildStr){
    //删除生成元数据
    if(buildStr==null){
    返回null;
    }
    字符串[]a=buildStr.split(\\+);
    如果(a.长度>0){
    返回[0];
    }否则{
    返回null;
    }
    }
    私有静态int比较字符串(字符串str1、字符串str2){
    int-ret=0;
    String[]verStr1=formatVersionString(str1.split(“\\”);
    String[]verStr2=formatVersionString(str2.split(“\\”);
    int i=0;
    //将索引设置为第一个不相等的序号或最短版本字符串的长度
    而(iprivate 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;
    }
    
    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