Indeed, that’s a scenario where you really really don’t want to use eval.
How constrained is the format? If sufficiently so, how about parsing the input manually? Assuming the dots and commata are the only “metacharacters”, you could do something like this:
my @range =
map { m{ (\d+) \s* \.\. \s* (\d+) }msx ? $1 .. $2 : /(\d+)/ }
split /,/, $range;
This breaks the string apart at the commata, then tries to match something that looks like a range; if it succeeds then it uses the regular range operator to make a real range out of it. If it fails, it returns the first sequence of digits it finds.
It should work correctly for any combination of ranges and single numbers strung together with commata. It’s also pretty lenient and will accept whitespace within ranges and any sort of non-digit garbage everywhere else – that might not be what you want, or might be just fine. Your call.
It will also not work for negative numbers and for fractional components. If you need to match more than just positive integers, the quickest and most robust way is probably to employ Regexp::Common.
Makeshifts last the longest. |