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

时间:2011-07-15 01:59:38

标签: java string compare

  

可能重复:
  How do you compare two version Strings in Java?

我有两个包含版本信息的字符串,如下所示:

str1 = "1.2"
str2 = "1.1.2"

现在,任何人都可以告诉我在Java和&amp ;;中比较字符串中这些版本的有效方法。返回0,如果它们相等,则返回-1,如果str1< str2&如果str1> str2。

,则为1

10 个答案:

答案 0 :(得分: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;
}    

答案 1 :(得分:12)

正如其他人所指出的那样,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
}

答案 2 :(得分:7)

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

的方法:

  1. 假设版本字符串中的节数(序数)的上限以及对此处表示的值的限制。通常最多4个点,任何序数最多999个。你可以看到它的发展方向,它正在转变版本以适应如下字符串:“1.0”=&gt; “001000000000”用字符串格式或其他方式填充每个序数。然后做一个字符串比较。
  2. 拆分序号分隔符('。')上的字符串并迭代它们并比较解析后的版本。这是Alex Gitelman所展示的方法。
  3. 在您从有问题的版本字符串中解析它们时比较序数。如果所有字符串实际上只是指向字符数组的指针,那么这将是一个明确的方法(你可以用它找到一个带有空终结符的'。'并移动一些2或4个指针。
  4. 对这三种方法的思考:

    1. blog post linked显示如何使用1.限制在版本字符串长度,部分数量和部分的最大值。我不认为这样的字符串在某一点上突破10,000就太疯狂了。此外,大多数实现仍然最终分割字符串。
    2. 提前拆分字符串很清楚,可以阅读和思考,但我们要经历每个字符串大约两次才能完成此操作。我想比较它与下一个方法的时间。
    3. 在分割时比较字符串,这样可以在以下对比中很快停止分割:“2.1001.100101.9999998”到“1.0.0.0.0.0.1.0.0.0.1”。如果这是C而不是Java,那么优势可能会继续限制为每个版本的每个部分分配给新字符串的内存量,但事实并非如此。
    4. 我没有看到有人提供第三种方法的例子,所以我想在此处添加它作为效率的答案。

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

      单元测试显示预期输出。

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

答案 3 :(得分:6)

这几乎可以肯定 最有效的方法,但鉴于版本号字符串几乎总是只有几个字符,我不认为值得进一步优化:

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

答案 4 :(得分:0)

将字符串拆分为“。”或者你的分隔符将是什么,然后将每个标记解析为整数值并进行比较。

int compareStringIntegerValue(String s1, String s2, String delimeter)  
{  
   String[] s1Tokens = s1.split(delimeter);  
   String[] s2Tokens = s2.split(delimeter);  

   int returnValue = 0;
   if(s1Tokens.length > s2Tokens.length)  
   {  
       for(int i = 0; i<s1Tokens.length; i++)  
       {  
          int s1Value = Integer.parseString(s1Tokens[i]);  
          int s2Value = Integer.parseString(s2Tokens[i]);  
          Integer s1Integer = new Integer(s1Value);  
          Integer s2Integer = new Integer(s2Value);  
          returnValue = s1Integer.compareTo(s2Value);
          if( 0 == isEqual)  
           {  
              continue; 
           }  
           return returnValue;  //end execution
        }
           return returnValue;  //values are equal
 } 

我将把另一个if语句留作练习。

答案 5 :(得分:0)

比较版本字符串可能是一团糟;你得到了无益的答案,因为做这项工作的唯一方法就是对你的订购惯例非常具体。我在blog post上看到了一个相对较短且完整的版本比较函数,代码放在公共域中 - 它不是Java,但是应该很容易看到如何适应它。

答案 6 :(得分:0)

改编自Alex Gitelman的回答。

int compareVersions( String str1, String str2 ){

    if( str1.equals(str2) ) return 0; // Short circuit when you shoot for efficiency

    String[] vals1 = str1.split("\\.");
    String[] vals2 = str2.split("\\.");

    int i=0;

    // Most efficient way to skip past equal version subparts
    while( i<vals1.length && i<val2.length && vals[i].equals(vals[i]) ) i++;

    // If we didn't reach the end,

    if( i<vals1.length && i<val2.length )
        // have to use integer comparison to avoid the "10"<"1" problem
        return Integer.valueOf(vals1[i]).compareTo( Integer.valueOf(vals2[i]) );

    if( i<vals1.length ){ // end of str2, check if str1 is all 0's
        boolean allZeros = true;
        for( int j = i; allZeros & (j < vals1.length); j++ )
            allZeros &= ( Integer.parseInt( vals1[j] ) == 0 );
        return allZeros ? 0 : -1;
    }

    if( i<vals2.length ){ // end of str1, check if str2 is all 0's
        boolean allZeros = true;
        for( int j = i; allZeros & (j < vals2.length); j++ )
            allZeros &= ( Integer.parseInt( vals2[j] ) == 0 );
        return allZeros ? 0 : 1;
    }

    return 0; // Should never happen (identical strings.)
}

你可以看到,不是那么微不足道。当你允许领先0时,这也会失败,但我从未见过版本“1.04.5”或w / e。您需要在while循环中使用整数比较来修复它。当您将字母与版本字符串中的数字混合时,这会变得更加复杂。

答案 7 :(得分:0)

Step1:在java中使用StringTokenizer并将点作为分隔符

StringTokenizer(String str, String delimiters)

您可以使用String.split()Pattern.split(),在点上拆分,然后使用Integer.parseInt(String str)将每个字符串转换为整数

第2步:从左到右比较整数。

答案 8 :(得分:0)

将它们拆分成数组然后进行比较。

// check if two strings are equal. If they are return 0;
String[] a1;

String[] a2;

int i = 0;

while (true) {
    if (i == a1.length && i < a2.length) return -1;
    else if (i < a1.length && i == a2.length) return 1;

    if (a1[i].equals(a2[i]) {
       i++;
       continue;
    }
     return a1[i].compareTo(a2[i];
}
return 0;

答案 9 :(得分:0)

我将把问题分成两部分,即格式化和比较。如果您可以假设格式正确,那么仅比较数字版本非常简单:

final int versionA = Integer.parseInt( "01.02.00".replaceAll( "\\.", "" ) );
final int versionB = Integer.parseInt( "01.12.00".replaceAll( "\\.", "" ) );

然后可以将两个版本作为整数进行比较。所以“大问题”是格式,但可以有很多规则。在我的情况下,我只完成至少两对数字,所以格式总是“99.99.99”,然后我做上述转换;所以在我的情况下,程序逻辑是格式化的,而不是版本比较。现在,如果你正在做一些非常具体的事情,也许你可以相信版本字符串的来源,也许你只需要检查版本字符串的长度然后只进行int转换...但我认为这是一个最好的做法确保格式符合预期。