在Java中比较版本string的有效方法
可能重复:
你如何比较Java中的两个版本string?
我有2个包含版本信息的string,如下所示:
str1 = "1.2" str2 = "1.1.2"
现在,任何一个人都可以告诉我在Java中如何比较这些版本的有效方法,如果它们相等,返回0,-1,如果str1 <str2&1,如果str1> str2。
/** * Compares two version strings. * * Use this instead of String.compareTo() for a non-lexicographical * comparison that works for version strings. eg "1.10".compareTo("1.6"). * * @note It does not work if "1.10" is supposed to be equal to "1.10.0". * * @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) { String[] vals1 = str1.split("\\."); String[] vals2 = str2.split("\\."); int i = 0; // set index to first non-equal ordinal or length of shortest version string while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) { i++; } // compare first non-equal ordinal number if (i < vals1.length && i < vals2.length) { int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])); return Integer.signum(diff); } // the strings are equal or one string is a substring of the other // eg "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4" return Integer.signum(vals1.length - vals2.length); }
正如其他人指出的那样,String.split()是一个非常简单的方法来进行比较,而Mike Deck使用这种(可能)短string可能不会有太大影响,嘿! 如果您想在不手动parsingstring的情况下进行比较,并且可以提早退出,则可以尝试java.util.Scanner类。
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()) return 1; //str1 has an additional lower-level version number return 0;
我正在寻找这样做,我看到三种不同的方法来做到这一点,到目前为止,几乎每个人都在分裂版本string。 我不认为这样做是有效的,尽pipe代码大小明智,它看起来不错,看起来不错。
处理办法:
- 假定一个版本string中的部分(序数)的数量的上限以及在那里表示的值的限制。 通常最多4个点,最多999个任何序数。 你可以看到这是怎么回事,它正在转换版本,以适应string,如:“1.0”=>“001000000000”string格式或其他方式填充每个序号。 然后做一个string比较。
- 在序号分隔符('。')上拆分string并迭代它们并比较parsing的版本。 Alex Gitelman很好地certificate了这一点。
- 比较序号,你parsing出来的版本string。 如果所有的string都只是指向字符数组的指针,那么这将是一个明确的方法(当你find一个空终止符,并且移动一些2或4个指针的时候,你可以用'。'来代替。
对三种方法的思考:
- 有一个博客文章链接 ,显示了如何去与1.限制是版本string的长度,部分的数量和最大值的部分。 我不认为有这样的一个string在一个点上打破了10000个是疯狂的。 另外,大多数实现仍然最终分割string。
- 事先把弦分开是清楚可读的,但是我们正在经历每个弦两次左右来做到这一点。 我想比较一下如何与下一个方法。
- 比较string,你可以很快的停止拆分,比较“2.1001.100101.9999998”到“1.0.0.0.0.0.1.0.0.0.1”。 如果这是C而不是Java,则优点可能会继续限制为每个版本的每个部分分配给新string的内存量,但事实上并非如此。
我没有看到有人给出了第三种方法的例子,所以我想在这里添加它作为提高效率的答案。
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 < 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; } }
显示预期产出的unit testing。
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")); } }
这几乎肯定不是 最有效的方法,但考虑到版本号string几乎总是只有几个字符长,我不认为这是值得进一步优化:
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); }
将string拆分为“。” 或者无论你的分隔符是什么,然后将这些标记的每一个parsing为整数值并进行比较。
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 }
我将把另一个陈述作为一个练习。
比较版本string可能是一团糟; 你正在得到无益的答案,因为做这项工作的唯一方法就是对你的订货惯例是非常具体的。 我在博客文章中看到了一个相对较短且完整的版本比较函数,代码放置在公共领域 – 它不在Java中,但应该很简单,看看如何适应这一点。
改编自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循环中使用整数比较来解决这个问题。 当您将字母与版本string中的数字混合时,这会变得更加复杂。
将它们拆分成数组,然后进行比较。
// 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;
我将把问题分成两部分,格式化和比较。 如果你可以假设格式是正确的,那么只比较数字版本是非常简单的:
final int versionA = Integer.parseInt( "01.02.00".replaceAll( "\\.", "" ) ); final int versionB = Integer.parseInt( "01.12.00".replaceAll( "\\.", "" ) );
然后这两个版本可以比较为整数。 所以“大问题”是格式,但是可以有很多规则。 在我的情况下,我只是完成至less两对数字,所以格式是“99.99.99”总是,然后我做上述转换; 所以在我的情况下,程序逻辑是在格式化,而不是在版本比较。 现在,如果你正在做一些非常具体的事情,也许你可以相信版本string的起源,也许你可以检查版本string的长度,然后做int转换…但我认为这是一个最佳做法确保格式符合预期。
步骤1:在java中使用StringTokenizer,以dot作为分隔符
StringTokenizer(String str, String delimiters)
或
您可以使用String.split()
和Pattern.split()
,在点上分割,然后使用Integer.parseInt(String str)
将每个String转换为Integer。
第2步:比较从左到右的整数。