how to compare two version strings in java

A

anywherenotes

Hi,

How would I compare two version strings, for example:
10.1.2.0 is greater than 10.0.0.0
and
10.1.2.0 is also greater than 9.0.0.0
however if I would compare those as String objects, obviously 9.0.0.0
would be greater than 10....

Is there a good way of doing it?
It's possible to split the string into numbers and do numeric
conversion, but if there's already a standard java class/method
handling this I'd like to use it.

Thank you.
 
S

Stefan Ram

Is there a good way of doing it?

When the version numbers are limited to 4 positions,
with values ranging from 0 to 99 for each:

public class Main
{
public static long value( final java.lang.String string )
{ if( string.contains( "." ))
{ final int index = string.lastIndexOf( "." );
return value( string.substring( 0, index ))* 100 +
value( string.substring( index + 1 )); }
else return java.lang.Long.valueOf( string ); }

public static void main( final java.lang.String[] args )
throws java.lang.Throwable
{ java.lang.System.out.println
( value( "10.1.2.0" ) > value( "9.0.0.0" )); }}

Otherwise, I would parse them into separate parts and
then put those parts into a comparable tuple:

http://www.purl.org/stefan_ram/html/ram.jar/de/dclj/ram/type/tuple/ComparableTuple.html

This allows for an arbitrary number of positions each
of an arbitrary size.
 
M

Mark Space

Hi,

How would I compare two version strings, for example:
10.1.2.0 is greater than 10.0.0.0
and
10.1.2.0 is also greater than 9.0.0.0
however if I would compare those as String objects, obviously 9.0.0.0
would be greater than 10....

Is there a good way of doing it?
It's possible to split the string into numbers and do numeric
conversion, but if there's already a standard java class/method
handling this I'd like to use it.

Thank you.

Yeah, I think you have to do the comparison yourself. I don't think
there's a standard class to do it for you.


package versionstring;


public class VersionString implements Comparable<VersionString> {

final private String version;

public VersionString(String version) {
if( version.length() == 0 )
throw new IllegalArgumentException(
"\"version\" may not be zero length" );
String temp[] = version.split("\\.");
for( String s : temp )
{
if( s == null )
throw new IllegalArgumentException(
"\"version\" contains a null version sub-string");
if( s.length() == 0 )
throw new IllegalArgumentException(
"\"version\" contains a zero lenght version sub-string" );
Integer.parseInt(s);
}
this.version = version;
}

@Override
public String toString()
{
return this.version;
}

@Override
public int compareTo(VersionString other)
{
if( other == this )
return 0;
if( other.version.equals( this.version ) )
return 0;
String otherVersionList[] = other.version.split("\\.");
String thisVersionList[] = this.version.split("\\.");
int thisIndex = 0;
for( String otherVersion : otherVersionList )
{
if( thisIndex >= thisVersionList.length )
break;
int thisVersionNum =
Integer.parseInt( thisVersionList[thisIndex] );
int otherVersionNum = Integer.parseInt(otherVersion);
if( thisVersionNum != otherVersionNum )
return (thisVersionNum - otherVersionNum < 0) ? -1 : 1;
thisIndex++;
}
if( thisVersionList.length != otherVersionList.length )
return (thisVersionList.length -
otherVersionList.length < 0) ? -1 : 1;
else
return 0;
}


/**
* @param args the command line arguments
*/
public static void main(String[] args) {
VersionString s10 = new VersionString("10");
VersionString s10b = new VersionString("10");
StringBuilder sb = new StringBuilder();
sb.append("1");
sb.append("0");
VersionString s10c = new VersionString( sb.toString() );

VersionString s10_9 = new VersionString("10.9");
VersionString s10_8 = new VersionString("10.8");
VersionString s9 = new VersionString("9");
VersionString s9_7 = new VersionString("9.7");

compare( s10, s9 );
compare( s9, s10 );
compare( s10, s10 );
compare( s10, s10b );
compare( s10, s10c );
compare( s9, s9 );
compare( s10, s10_9 );
compare( s10_9, s10 );
compare( s10_8, s10_9 );
compare( s10_9, s10_8 );
compare( s9_7, s10_8 );
compare( s10, s9_7 );
compare( s9_7, s10 );
try {
compare( s9_7, null );
} catch( Exception ex )
{ System.out.println( ex ); }

construct( null );
construct( "" );
construct( "abc" );
construct( "10.9a" );
construct( "7.0" );
construct( "6." );
construct( ".1" );

}

private static void compare( VersionString s1, VersionString s2 ) {
System.out.print("Compare " + s1 + " and " + s2 + " : ");
System.out.println( s1.compareTo(s2) );
}

private static void construct( String s ) {
System.out.print("Constructing \"" +s+ "\" - " );
try {
new VersionString( s );
} catch( Exception ex )
{ System.out.print( ex ); }
finally { System.out.println(""); }
}
}
 
S

Stefan Ram

Eric Sosman said:
Unfortunately, it reports value("2.0") < value("1.5.1") ...
¯¯
»It« does not seem to refer to the last sentence quoted: »This
allows for an arbitrary number of positions each of an
arbitrary size.«, but to the program above it. You can make
this more clear by not quoting the final part of my post that
you do not refer to, but only that part you directly refer to.

Here is a new version of that program:

public class Main
{
public static long value( final java.lang.String string )
{ final int index = string.indexOf( "." );
if( index > -1 )
{ return value( string.substring( 0, index ))+
value( string.substring( index + 1 ))/ 100; }
else return java.lang.Long.valueOf( string )* 100000000l; }

public static void main( final java.lang.String[] args )
throws java.lang.Throwable
{ java.lang.System.out.println
( value( "10.1.2.0" )> value( "9.0.0.0" ));
java.lang.System.out.println
( value( "2.0" )> value( "1.5.1" )); }}

true
true
 
D

Daniele Futtorovic

Hi,

How would I compare two version strings, for example:
10.1.2.0 is greater than 10.0.0.0
and
10.1.2.0 is also greater than 9.0.0.0
however if I would compare those as String objects, obviously 9.0.0.0
would be greater than 10....

Is there a good way of doing it?
It's possible to split the string into numbers and do numeric
conversion, but if there's already a standard java class/method
handling this I'd like to use it.

Thank you.

For the fun of it:

<code>
package scratch;

import java.util.*;

public class Scratch
{
private static class VersionStringComparator
implements Comparator<String>
{
public int compare(String s1, String s2){
if( s1 == null && s2 == null )
return 0;
else if( s1 == null )
return -1;
else if( s2 == null )
return 1;

String[]
arr1 = s1.split("[^a-zA-Z0-9]+"),
arr2 = s2.split("[^a-zA-Z0-9]+")
;

int i1, i2, i3;

for(int ii = 0, max = Math.min(arr1.length, arr2.length);
ii <= max; ii++){
if( ii == arr1.length )
return ii == arr2.length ? 0 : -1;
else if( ii == arr2.length )
return 1;

try{
i1 = Integer.parseInt(arr1[ii]);
}
catch (Exception x){
i1 = Integer.MAX_VALUE;
}

try{
i2 = Integer.parseInt(arr2[ii]);
}
catch (Exception x){
i2 = Integer.MAX_VALUE;
}

if( i1 != i2 ){
return i1 - i2;
}

i3 = arr1[ii].compareTo(arr2[ii]);

if( i3 != 0 )
return i3;
}

return 0;
}
}

public static void main(String[] ss){

String[] data = new String[]{
"2.0",
"1.5.1",
"10.1.2.0",
"9.0.0.0",
"2.0.0.16",
"1.6.0_07",
"1.6.0_07-b06",
"1.6.0_6",
"1.6.0_07-b07",
"1.6.0_08-a06",
"5.10",
"Generic_127127-11",
"Generic_127127-13"
};

List<String> list = Arrays.asList(data);
Collections.sort(list, new VersionStringComparator());

for(String s: list)
System.out.println(s);
}
}
</code>

<output>
1.5.1
1.6.0_6
1.6.0_07
1.6.0_07-b06
1.6.0_07-b07
1.6.0_08-a06
2.0
2.0.0.16
5.10
9.0.0.0
10.1.2.0
Generic_127127-11
Generic_127127-13
</output>
 
T

Tom Anderson

There's no universally-agreed syntax for version strings. I'm
composing this message on Thunderbird "2.0.0.16", my Java version is
"1.6.0_07" (also known as "1.6.0_07-b06"), I use an O/S whose version is
"5.10" and whose kernel patch level is "Generic_127127-11".

Okay, rambling follows.

The Mac, in classial period at least, had a well-defined standard for
version numbering, used for system components and applications, both Apple
and third-party, based on the NumVersion struct in MacTypes.h:

struct NumVersion {
UInt8 majorRev; /*1st part of version number in BCD*/
UInt8 minorAndBugRev; /*2nd & 3rd part of version number share a byte*/
UInt8 stage; /*stage code: dev, alpha, beta, final*/
UInt8 nonRelRev; /*revision level of non-released version*/
};

This basically gives you a three-part version number, major.minor.bug,
where the major version is two digits, and the minor and bug are one digit
(like, say 10.5.4). Apple had conventions about when to increment each
number, but i don't think it followed them very rigorously.

As well as that, you get a flag which indicates if this is a proper
release, or a development, alpha, beta, or release candidate version, and
a revision number within that scope, which ranges from 0 to 255, although
by convention, there aren't revisions within a release, only at earlier
stages.

The format for writing this out was to have the dotted major.minor.bug
part, then a single letter for stage, and a number for the revision level;
So, you might have the following sequence of versions:

10.5.5d1
10.5.5d2
10.5.5d3
10.5.5a1
10.5.5a2
10.5.5b1
10.5.5b2
10.5.5rc1
10.5.5

With the last one being the release stage.

A nifty feature of this structure was that it fitted in 32 bits, and is
laid out most-to-least significant (on a big-endian machine, anyway),
which means that if you reinterpret it as a 32-bit unsigned integer, the
ordering relation is preserved. That makes comparison as easy as a cast -
no parsing or elementwise comparison needed!

There was a hiccup, though, in that there weren't actually separate stage
values for release candidate and release - a genuine release was marked by
having a revision number of zero. That means that a naive comparison will
make release versions look older than all the release candidates. Fairly
simple to work around, but annoying. It would have been really simple to
define a new stage constant for release candidates, in between the beta
and release values. For some reason, this was never done.

Still, it was kind of nifty.
I see no solution for the O.P. other than to know things about the
syntax of the version strings he's interested in, to parse them into
their constituent pieces, and to compare piece by piece. The
java.util.regex package may be helpful for the parsing, but I know of no
way to write a "universal" regex for all styles of versioning.

I got the impression that the OP *did* know the specific format; it was a
string of decimal numbers, separated by full stops. But you're right, even
if he does know that, there's nothing for it but to parse and compare.

In python, FWIW:

def compareVersions(aStr, bStr):
a, b = map(lambda s: tuple(map(int, s.split("."))), (aStr, bStr))
return cmp(a, b)

It may take a few more lines of code in java.

tom
 
S

Stefan Ram

Tom Anderson said:
def compareVersions(aStr, bStr):
a, b = map(lambda s: tuple(map(int, s.split("."))), (aStr, bStr))
return cmp(a, b)
It may take a few more lines of code in java.

Here is a Java implementation using ram.jar:

public class Main
{
public static void main( final java.lang.String[] args )
{
java.lang.System.out.println
( new de.dclj.ram.type.tuple.DefaultComparableTuple
( "231.46357.31e.45e.2351".split( "\\." )).compareTo
( new de.dclj.ram.type.tuple.DefaultComparableTuple
( "231.46357.31e.45f.2351".split( "\\." )))); }}

-1

Using »aStr« and »bStr« and an import statement,
the expression within the println call can be written as

new DefaultComparableTuple( aStr.split( "\\." )).compareTo
( new DefaultComparableTuple( bStr.split( "\\." )))
 
S

Stefan Ram

new DefaultComparableTuple( aStr.split( "\\." )).compareTo
( new DefaultComparableTuple( bStr.split( "\\." )))

I now become aware, that this will compare the parts
between the periods using a string comparison.

To get a numeric comparison, the array obtained by split( "\\." )
still needs to be converted to java.lang.Integer[].
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top