Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Comparing decimal values

by c (Hermit)
on Jan 21, 2003 at 15:31 UTC ( #228707=perlquestion: print w/replies, xml ) Need Help??

c has asked for the wisdom of the Perl Monks concerning the following question:

I've written a script that runs a couple of different routines based on the version of another application resident on the server. Up until now, I've done

my $versionFile = '/usr/local/AppName/version'; open(FH,$versionFile) or warn "No version file"; my $version = <FH>; $version =~ s/\.//g; if $version > 140 { ... }

Generally the App versions were in the format x.y.z, however I've found a few systems runninig x.y.zz meaning that these versions may appear "newer" when compared through my process (e.g. 1.4.10 would be greater than 1.6.0).
What would be the better way of achieving this comparison? When I just try comparing the two directly, I receive an error saying that comparison is not numeric.

-c

Replies are listed 'Best First'.
Re: Comparing decimal values
by broquaint (Abbot) on Jan 21, 2003 at 15:55 UTC
    If you want to do any sort of version number trickery then I'd recommend John Peacock's version module e.g
    use version; my $v1 = version->new("1.4.10"); my $v2 = version->new("1.4.6"); print $v1->stringify," > ",$v2->stringify," = ", ($v1 > $v2 ? "true" : "false"), $/; __output__ 1.4.10 > 1.4.6 = true

    HTH

    _________
    broquaint

Re: Comparing decimal values
by blokhead (Monsignor) on Jan 21, 2003 at 15:42 UTC
    You have to split the version numbers into pieces and compare each part. Here's a very wordy solution:
    my $old_version = '1.4.10'; my $new_version = '1.6.0'; my @old_version = split /\./, $old_version; my @new_version = split /\./, $new_version; my $cmp = 0; $cmp ||= ($old_version[$_] <=> $new_version[$_]) for (0 .. $#old_versi +on); if ($cmp == 0) { print "$old_version == $new_version\n"; } elsif ($cmp == 1) { print "$old_version > $new_version\n"; } else { print "$old_version < $new_version\n"; }

    blokhead

Re: Comparing decimal values
by Hofmator (Curate) on Jan 21, 2003 at 16:26 UTC
    ... or use Sort::Versions; from the synopsis:
    use Sort::Versions; @l = sort { versioncmp($a, $b) } qw( 1.2 1.2.0 1.2a.0 1.2.a 1.a 02.a ) +;

    -- Hofmator

Re: Comparing decimal values
by Molt (Chaplain) on Jan 21, 2003 at 16:26 UTC

    I'd agree with broquaint above about using the module, but if that's not do-able for some reason a way around this is to take the string, split it into it's components, zero-pad these to a long length, rejoin them, and compare on the end results. Note that this will only work if they have the same number of components, 1.1.5 > 2.0.

    The code below incorporates this into a sort routine. If this was to be used for big arrays I'd recommend doing a Schwartzian Transformation using it to avoid the overhead, but for small arrays this shouldn't be too much of a problem.

    #!/usr/bin/perl use strict; use warnings; my @test_data = qw(2.0.0 1.2.0 1.2.10 1.1.15 1.2.1 1.2.0); print "$_\n" foreach sort {cmp_versions($a,$b)} @test_data; sub cmp_versions { my ($a, $b) = @_; $a = join '', map { sprintf '%08d', $_ } split /\./, $a; $b = join '', map { sprintf '%08d', $_ } split /\./, $b; return $a cmp $b; }
Re: Comparing decimal values
by BrowserUk (Pope) on Jan 21, 2003 at 17:17 UTC

    Concatenating 'v' to the front of each version string and evaling using eq, gt,lt or cmp seems to work okay. I'm sure I saw 'vMMM.nnn.rr' described somewhere in the docs, but I couldn't find it anywhere just now?

    #! perl -slw use strict; sub map2 (&@) { use Carp; my $code = shift; croak 'Odd number of values in list' if @_ & 1; map { local ($a, $b) = (shift,shift); $code->() } 1 .. (@_>>1); } sub cmpVer{ eval{'v'.$_[0] cmp 'v'.$_[1];} } # << This is the salient +bit my @a = qw[ 1.1.1 1.1.1 1.1.1 1.1.2 1.1.2 1.1.1 1.1.10 1.2.0 0.111.0 9.0.0 9.0.0 0.111.0 1.4.999 1.5.0 ]; map2{ printf "%10s %2.2s %s\n", $a, (qw/== > </)[cmpVer($a, $b)], $b; } @a; __END__ C:\test>228707 1.1.1 == 1.1.1 1.1.1 < 1.1.2 1.1.2 > 1.1.1 1.1.10 < 1.2.0 0.111.0 < 9.0.0 9.0.0 > 0.111.0 1.4.999 < 1.5.0 C:\test>

    If anyone can tell me why I can't supply a qw// list directly to my map2 sub

    map2{ print $a, $b; } qw[a b c d];

    without getting syntax errors, I'd like to hear the explanation.


    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://228707]
Approved by Tanalis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (5)
As of 2021-06-15 12:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What does the "s" stand for in "perls"? (Whence perls)












    Results (72 votes). Check out past polls.

    Notices?