Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I expand a string like "1..15,16" into a list of numbers?

I have a Perl application that takes from command line an input as:

application --fields 1-6,8

I am required to display the fields as requested by the user on command line.

I thought of substituting '-' with '..' so that I can store them in array e.g.

$str = "1..15,16" ;
@arr2 = ( $str ) ;
@arr = ( 1..15,16 ) ;
print "@arr\n" ;
print "@arr2\n" ;

The problem here is that @arr works fine ( as it should ) but in @arr2 the entire string is not expanded as array elements.

I have tried using escape sequences but no luck.

Can it be done this way?

like image 269
sud03r Avatar asked Jun 19 '09 07:06

sud03r


3 Answers

If this is user input, don't use string eval on it if you have any security concerns at all.

Try using Number::Range instead:

 use Number::Range;

 $str = "1..15,16" ;
 @arr2 = Number::Range->new( $str )->range;
 print for @arr2;

To avoid dying on an invalid range, do:

 eval { @arr2 = Number::Range->new( $str )->range; 1 } or your_error_handling

There's also Set::IntSpan, which uses - instead of ..:

 use Set::IntSpan;

 $str = "1-15,16";
 @arr2 = Set::IntSpan->new( $str )->elements;

but it requires the ranges to be in order and non-overlapping (it was written for use on .newsrc files, if anyone remembers what those are). It also allows infinite ranges (where the string starts -number or ends number-), which the elements method will croak on.

like image 179
ysth Avatar answered Oct 04 '22 21:10

ysth


You're thinking of @arr2 = eval($str); Since you're taking input and evaluating that, you need to be careful. You should probably @arr2 = eval($str) if ($str =~ m/^[0-9.,]+$/)

P.S. I didn't know about the Number::Range package, but it's awesome. Number::Range ftw.

like image 22
dlamblin Avatar answered Oct 04 '22 21:10

dlamblin


I had the same problem in dealing with the output of Bit::Vector::to_Enum. I solved it by doing:

$range_string =~ s/\b(\d+)-(\d+)\b/expand_range($1,$2)/eg;

then also in my file:

sub expand_range
{
    return join(",",($_[0] .. $_[1]));
}

So "1,3,5-7,9,12-15" turns into "1,3,5,6,7,9,12,13,14,15".

I tried really hard to put that expansion in the 2nd part of the s/// so I wouldn't need that extra function, but I couldn't get it to work. I like this because while Number::Range would work, this way I don't have to pull in another module for something that should be trivial.

like image 20
kbrannen Avatar answered Oct 04 '22 20:10

kbrannen