Maybe (maybe), you can get away by splitting all search terms into non-digits and digits, and sorting on that. This means that all items (in a STB population) have the same number of items, and that nobody mixes letters and numbers within the item:
#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 6;
sub natural_string_cmp {
my ($left, $right) = @_;
my @left = split /\b|(?<=[A-Za-z])(?=\d)|(?<=\d)(?=[A-Za-z])/, $l
+eft;
my @right = split /\b|(?<=[A-Za-z])(?=\d)|(?<=\d)(?=[A-Za-z])/, $r
+ight;
#use Data::Dumper;
#warn Dumper \@left;
#warn Dumper \@right;
# Now, reconstruct a string for each item that can simply be compa
+red directly.
# For this, we zero-left-pad all numbers and extend all strings wi
+th \0
# For simplicity, I assume no number longer than 18 digits and no
+string longer than 10 characters
my $l = join "", map { /\d/ ? sprintf '%018d', $_ : substr($_."\0\
+0\0\0\0\0\0\0\0\0",0,10) } @left;
my $r = join "", map { /\d/ ? sprintf '%018d', $_ : substr($_."\0\
+0\0\0\0\0\0\0\0\0",0,10) } @right;
#warn $l;
#warn $r;
return $l cmp $r
}
is natural_string_cmp('Rack1-Unit1', 'Rack1-Unit1'), 0, "Identity";
is natural_string_cmp('Rack1-Unit1', 'Rack1-Unit2'), -1, "Smaller";
is natural_string_cmp('Rack1-Unit2', 'Rack3-Unit1'), -1, "Smaller";
is natural_string_cmp('Rack3-Unit1', 'Rack1-Unit1'), 1, "Larger";
is natural_string_cmp('R1U1', 'R1U1'), 0, "Identity";
is natural_string_cmp('R1U2', 'R3U1'), -1, "Smaller";
is natural_string_cmp('R3U1', 'R10U1'), -1, "Smaller";
is natural_string_cmp('R10U1', 'R3U1'), 1, "Larger";
#R1-U1
#R1-U10
#R2-U2
#R3-U13
#R10-U1
#R10-U5
#R10-U11
#