'Compare 3 digit dot separated number with >3 digit and alphanumeric in Perl

Can anybody help to perform the below operation in Perl?

  1. Read a set of numbers or chars from a file (ex. 2021.2.3.0 / 2.8.1 / 2021.3.1_alpha ).
  2. Compare it with number 2.4.0. (always the same number) if greater or smaller.
  3. Ex: (2.4.0 < 2021.2.3.0) --> this is false. so do nothing.
  4. if (2.4.0 > 2.3.0) --> True - So do some operation.
  5. For 3 digit numbers with dot separated we can do this. (actually we are doing).
  6. But how to compare for digits and alpha numeric which has more that 3 digits.

Here i need to always compare with 2.4.0. Can any one guide me on how to do this in Perl?

My existing code is below:

use strict;
use warnings;
my $path_ice = $ARGV[0];
my $filename = ($#ARGV == 0) ?  $ARGV[0]."\\medc17_tools.ini" : $ARGV[1];
my $flag = 0;
my $buld_ver = "";
my @buld_very = "";
my $status = 55;
if (open(my $fh, '<:encoding(UTF-8)', $filename))
 {
 while (my $line = <$fh>)
   {
   chomp $line;
   if($line=~m/^PRJ_BUILD_NAME=mdgb/) {$flag = 1;}
   if($line=~m/^PRJ_BUILD_VERSION=/)
     {
     @buld_very = split('=',$line);
     $buld_ver = substr($buld_very[1], 0, 5);
     $buld_ver =~ tr/.//d;
     if ($buld_ver < 240 ) {$status = 44;}
     last;
     }
   }
} 
else {
      warn "Could not open file '$filename' $!";
    }
if ($flag == 1)
{
exit(1);
}
else
   {
   exit(0);
    }

Here we are converting dot separated to whole number and comparing. It works well for 3 digit values. But don't know how to handle for alphanumeric and >3 digit dot separated numbers.

Any help?



Solution 1:[1]

For versions that contain both numbers and non-numeric strings one tool is Sort::Versions

use Sort::Versions;

my @sorted versions = sort versioncmp qw( 2021.2.3.0  2.8.1  2021.3.1_alpha );

In very very old Perls (pre-5.6) use sort { versioncmp($a, $b) } LIST

Or compare individually

if (versioncmp('2.4.0', '2.4.0_a') == -1) { ... }  # first is "lesser"

The criterion is

... the two strings are treated as subunits delimited by periods or hyphens. Each subunit can contain any number of groups of digits or non-digits. If digit groups are being compared on both sides, a numeric comparison is used, otherwise a ASCII ordering is used. A group or subgroup with more units will win if all comparisons are equal. A period binds digit groups together more tightly than a hyphen.


The first example above (the sort), when printed produces

2.8.1
2021.2.3.0
2021.3.1_alpha

But a bulleted example in the questions says

Ex: (2.4.0 < 2021.2.3.0) --> this is false. so do nothing.

It has been clarified in comments that in a version string like 2021.2.3.0 that leading 2021 is a year; the rest of the string is then compared to a conventional format like 2.4.0.

My additional assumptions: later year wins, regardless of the rest, and if only one version has a year then that one is newer.

A simple sub suffices

use Sort::Versions qw(versioncmp);   # imported by default, too

sub cmp_versions {
    my ($v1, $v2) = @_; 
    #croak "Usage: ", (caller(0))[3], " version-string version-string" 
    #   if not $v1 or not $v2;

    # Extract and collect years (need 'undef' when no year)
    my @years = map { /^([0-9]{4})\./ // undef } $v1, $v2;

    # Larger year wins; if same, or both absent, compare versions
    if ($years[0] and $years[1]) { 
        return $years[0] <=> $years[1] || versioncmp($v1, $v2)
    }   
    elsif ($years[0]) { return  1 } 
    elsif ($years[1]) { return -1 } 
    else              { versioncmp($v1, $v2) }     
}

A test program

use warnings;
use strict;
use feature 'say';

#sub cmp_versions { ... }    # From above

my ($v1, $v2) = @ARGV;
$v1 //= '2021.2.0';
$v2 //= '2021.2.0_b';  #--> -1 (the first one considered earlier)

say cmp_versions($v1, $v2);

If sorting versions the same can be used

use warnings;
use strict;
use feature 'say';

use Sort::Versions;

sub sort_versions {    
    return sort {
        my $ya = $a =~ /^([0-9]{4})\./;
        my $yb = $b =~ /^([0-9]{4})\./;
    
        if ($ya and $yb) {
            $ya <=> $yb || versioncmp($a, $b)
        }
        elsif ($ya) {  1 }
        elsif ($yb) { -1 }
        else        { versioncmp($a, $b) }
    }
    @_;
}

my @versions = qw(2.4.0 2021.2 2.4.0.0 2022.1.1 2021.2_a 2.6);

say for sort_versions( @versions );

Output

2.4.0
2.4.0.0
2.6
2021.2
2021.2_a
2022.1.1

This conforms to the described or mentioned criteria, some clarified in comments.

As for yet other possible (unstated) details -- a library can only do so much to make sense of any given versioning scheme, or to guess the intended comparison criteria. For very particular criteria the strings to compare may need to first be preprocessed into a more conventional scheme, or the "tricky" ones can be compared by hand.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1