在Java中比较版本字符串的有效方法

Mik*_*ike 57 java string compare

可能重复:
你如何比较Java中的两个版本字符串?

我有2个字符串,其中包含版本信息,如下所示:

str1 = "1.2"
str2 = "1.1.2"
Run Code Online (Sandbox Code Playgroud)

现在,任何人都能告诉我在Java中字符串中比较这些版本的有效方法并返回0,如果它们相等,则为-1,如果str1 <str2则为1,如果str1> str2则为1.

Ale*_*man 146

对于字符串操作,需要commons-lang3-3.8.1.jar.

/**
 * Compares two version strings. 
 * 
 * Use this instead of String.compareTo() for a non-lexicographical 
 * comparison that works for version strings. e.g. "1.10".compareTo("1.6").
 * 
 * @param v1 a string of alpha numerals separated by decimal points. 
 * @param v2 a string of alpha numerals separated by decimal points.
 * @return The result is 1 if v1 is greater than v2. 
 *         The result is 2 if v2 is greater than v1. 
 *         The result is -1 if the version format is unrecognized. 
 *         The result is zero if the strings are equal.
 */

public int VersionCompare(String v1,String v2)
{
    int v1Len=StringUtils.countMatches(v1,".");
    int v2Len=StringUtils.countMatches(v2,".");

    if(v1Len!=v2Len)
    {
        int count=Math.abs(v1Len-v2Len);
        if(v1Len>v2Len)
            for(int i=1;i<=count;i++)
                v2+=".0";
        else
            for(int i=1;i<=count;i++)
                v1+=".0";
    }

    if(v1.equals(v2))
        return 0;

    String[] v1Str=StringUtils.split(v1, ".");
    String[] v2Str=StringUtils.split(v2, ".");
    for(int i=0;i<v1Str.length;i++)
    {
        String str1="",str2="";
        for (char c : v1Str[i].toCharArray()) {
            if(Character.isLetter(c))
            {
                int u=c-'a'+1;
                if(u<10)
                    str1+=String.valueOf("0"+u);
                else
                    str1+=String.valueOf(u);
            }
            else
                str1+=String.valueOf(c);
        }            
        for (char c : v2Str[i].toCharArray()) {
            if(Character.isLetter(c))
            {
                int u=c-'a'+1;
                if(u<10)
                    str2+=String.valueOf("0"+u);
                else
                    str2+=String.valueOf(u);
            }
            else
                str2+=String.valueOf(c);
        }
        v1Str[i]="1"+str1;
        v2Str[i]="1"+str2;

            int num1=Integer.parseInt(v1Str[i]);
            int num2=Integer.parseInt(v2Str[i]);

            if(num1!=num2)
            {
                if(num1>num2)
                    return 1;
                else
                    return 2;
            }
    }
    return -1;
}    
Run Code Online (Sandbox Code Playgroud)

  • 当谈到1.3a.1 verus 1.3b:D和1.3-SNAPSHOT; D时,它会变得更有趣 (4认同)
  • 请注意,如果版本字符串格式不正确,Integer.valueOf()可能会转换NumberFormatException. (2认同)

Eri*_*ric 14

正如其他人所指出的那样,String.split()是一种非常简单的方法来进行你想要的比较,而Mike Deck则提出了一个很好的观点,即使用这样的(可能的)短字符串,它可能无关紧要,但是嘿!如果您想在不手动解析字符串的情况下进行比较,并且可以选择提前退出,则可以尝试java.util.Scanner类.

public static int versionCompare(String str1, String str2) {
    try ( Scanner s1 = new Scanner(str1);
          Scanner s2 = new Scanner(str2);) {
        s1.useDelimiter("\\.");
        s2.useDelimiter("\\.");

        while (s1.hasNextInt() && s2.hasNextInt()) {
            int v1 = s1.nextInt();
            int v2 = s2.nextInt();
            if (v1 < v2) {
                return -1;
            } else if (v1 > v2) {
                return 1;
            }
        }

        if (s1.hasNextInt() && s1.nextInt() != 0)
            return 1; //str1 has an additional lower-level version number
        if (s2.hasNextInt() && s2.nextInt() != 0)
            return -1; //str2 has an additional lower-level version 

        return 0;
    } // end of try-with-resources
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*eck 8

这是几乎可以肯定不是去做有效的方式,但考虑到版本号的字符串几乎总是只有几个字符长,我不认为这是值得进一步优化:

public static int compareVersions(String v1, String v2) {
    String[] components1 = v1.split("\\.");
    String[] components2 = v2.split("\\.");
    int length = Math.min(components1.length, components2.length);
    for(int i = 0; i < length; i++) {
        int result = new Integer(components1[i]).compareTo(Integer.parseInt(components2[i]));
        if(result != 0) {
            return result;
        }
    }
    return Integer.compare(components1.length, components2.length);
}
Run Code Online (Sandbox Code Playgroud)

  • 修正了Ed Staub和@RedLenses指出的错误.只需2年就能得到正确答案! (2认同)
  • 为了简洁而投票,但它无法正确比较“1.0.0”和“1.0”。返回值是“1”,而它应该是“0” (2认同)

dla*_*lin 8

我本人希望自己这样做,我看到了三种不同的方法,到目前为止,每个人都在分割版本字符串.我不认为这样做是有效的,虽然代码大小明智,它读得很好,看起来很好.

处理办法:

  1. 假设版本字符串中的部分(序数)的数量的上限以及对那里表示的值的限制.通常最多4个点,任何序数最多999个.你可以看到它的发展方向,并且它将转换为适合字符串的字符串:"1.0"=>"001000000000",使用字符串格式或其他方式来填充每个序数.然后做一个字符串比较.
  2. 拆分序号分隔符('.')上的字符串并迭代它们并比较解析后的版本.这是Alex Gitelman所展示的方法.
  3. 当您从有问题的版本字符串解析它们时比较序数.如果所有字符串实际上只是指向字符数组的指针,那么这将是一个明确的方法(在这里您可以用找到的null终止符替换'.'并移动一些2或4个指针.

关于这三种方法的想法:

  1. 有一个链接博客文章显示了如何使用1.限制在版本字符串长度,部分数量和部分的最大值.我不认为这样的字符串在某一点上突破10,000就太疯狂了.此外,大多数实现仍然最终分割字符串.
  2. 事先拆分字符串是很清楚的阅读和思考,但我们要经历每个字符串大约两次来做这件事.我想比较它与下一个方法的时间.
  3. 在分割时比较字符串可以让您在比较"2.1001.100101.9999998"到"1.0.0.0.0.0.1.0.0.0.1"的过程中尽早停止分割.如果这是C而不是Java,那么优势可能会继续限制为每个版本的每个部分分配给新字符串的内存量,但事实并非如此.

我没有看到有人给出第三种方法的例子,所以我想在这里添加它作为效率的答案.

public class VersionHelper {

    /**
     * Compares one version string to another version string by dotted ordinals.
     * eg. "1.0" > "0.09" ; "0.9.5" < "0.10",
     * also "1.0" < "1.0.0" but "1.0" == "01.00"
     *
     * @param left  the left hand version string
     * @param right the right hand version string
     * @return 0 if equal, -1 if thisVersion &lt; comparedVersion and 1 otherwise.
     */
    public static int compare(@NotNull String left, @NotNull String right) {
        if (left.equals(right)) {
            return 0;
        }
        int leftStart = 0, rightStart = 0, result;
        do {
            int leftEnd = left.indexOf('.', leftStart);
            int rightEnd = right.indexOf('.', rightStart);
            Integer leftValue = Integer.parseInt(leftEnd < 0
                    ? left.substring(leftStart)
                    : left.substring(leftStart, leftEnd));
            Integer rightValue = Integer.parseInt(rightEnd < 0
                    ? right.substring(rightStart)
                    : right.substring(rightStart, rightEnd));
            result = leftValue.compareTo(rightValue);
            leftStart = leftEnd + 1;
            rightStart = rightEnd + 1;
        } while (result == 0 && leftStart > 0 && rightStart > 0);
        if (result == 0) {
            if (leftStart > rightStart) {
                return containsNonZeroValue(left, leftStart) ? 1 : 0;
            }
            if (leftStart < rightStart) {
                return containsNonZeroValue(right, rightStart) ? -1 : 0;
            }
        }
        return result;
    }

    private static boolean containsNonZeroValue(String str, int beginIndex) {
        for (int i = beginIndex; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c != '0' && c != '.') {
                return true;
            }
        }
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试显示预期输出.

public class VersionHelperTest {

    @Test
    public void testCompare() throws Exception {
        assertEquals(1, VersionHelper.compare("1", "0.9"));
        assertEquals(1, VersionHelper.compare("0.0.0.2", "0.0.0.1"));
        assertEquals(1, VersionHelper.compare("1.0", "0.9"));
        assertEquals(1, VersionHelper.compare("2.0.1", "2.0.0"));
        assertEquals(1, VersionHelper.compare("2.0.1", "2.0"));
        assertEquals(1, VersionHelper.compare("2.0.1", "2"));
        assertEquals(1, VersionHelper.compare("0.9.1", "0.9.0"));
        assertEquals(1, VersionHelper.compare("0.9.2", "0.9.1"));
        assertEquals(1, VersionHelper.compare("0.9.11", "0.9.2"));
        assertEquals(1, VersionHelper.compare("0.9.12", "0.9.11"));
        assertEquals(1, VersionHelper.compare("0.10", "0.9"));
        assertEquals(0, VersionHelper.compare("0.10", "0.10"));
        assertEquals(-1, VersionHelper.compare("2.10", "2.10.1"));
        assertEquals(-1, VersionHelper.compare("0.0.0.2", "0.1"));
        assertEquals(1, VersionHelper.compare("1.0", "0.9.2"));
        assertEquals(1, VersionHelper.compare("1.10", "1.6"));
        assertEquals(0, VersionHelper.compare("1.10", "1.10.0.0.0.0"));
        assertEquals(1, VersionHelper.compare("1.10.0.0.0.1", "1.10"));
        assertEquals(0, VersionHelper.compare("1.10.0.0.0.0", "1.10"));
        assertEquals(1, VersionHelper.compare("1.10.0.0.0.1", "1.10"));
    }
}
Run Code Online (Sandbox Code Playgroud)