http://qs321.pair.com?node_id=99825

A common version number contains sequences of digits, sequences of letters, and punctuation marks.

The string is viewed as a sequence of components. The components are compared with corresponding components, from left-to-right.

A component is one of the following:

  • A series of digits. They are compared as numbers (“11” > “2”).
  • A series of ASCII letters. They are compared as strings, case-insensitive.
  • A series of punctuation marks, loosely defined as everything else. They are compared as match/no-match only. A no-match is considered an exception, and prevents greater/less comparisons from working at all.

The parts are compared from left-to-right. If the parts match up to the point where one string runs out of parts, the one with parts left over is Larger. For example, “1.23.45” < “1.23.45b”. The left string has 5 parts (3 series’ of digits, two series’ of punctuation marks); the right string has 6 parts.

Strings that don’t work with this algorithm are those that use words, dates, or non-left-to-right ordering of parts.

This was written before Perl provided the "v-string" literal. But, it is not limited to just numbers and dots. It handles most any reasonable version naming system, including other delimiters and letters as well as numbers.

{ my $splitter= qr/\d+|[A-Za-z]+|[^0-9A-Za-z]+/; sub compare_version_string ($$) # returns <0, 0, >0 to indicate less, eq, or greater-than. # dies (exception) if no relation exists. { my ($left, $right)= @_; my @left= $left =~ /$splitter/g; my @right= $right =~ /$splitter/g; print "@left\n"; print "@right\n"; my $r; while (@left && @right) { $left= shift @left; $right= shift @right; if ($left =~ /^\d+$/ && $right =~ /^\d+$/) { # compare as numbers $r= $left <=> $right; return $r if $r; # or keep going if zero. } elsif ($left =~ /^[A-Za-z]+$/ && $right =~ /^[A-Za-z]+$/) { # compare as strings $r= $left cmp $right; return $r if $r; # or keep going if zero. } elsif ($left =~ /^[^0-9A-Za-z]+$/ && $right =~ /^[^0-9A-Za-z]+$/ +) { # delimter or "other", much match exact. die "version strings are not compatible.\n" unless $left eq $r +ight; } else { # the parts are not of the same type die "version strings are not compatible.\n"; } } # one of the strings ran out of parts. return scalar(@left) <=> scalar(@right); } }