Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I am looking for some code that will break-down 'ranges' of inventorynumbers, that may consists of just numbers or a combination of some characters and numbers, into individual inventorynumbers. Just to give you an example: ph1-3;25;t3;t47-t50;d4-6 needs to be broken down to: ph1 ph2 ph3 25 t3 t47 t48 t49 t50 d4 d5 d6 Any suggestions are greatly appreciated.

Replies are listed 'Best First'.
Re: break-down ranges
by BrowserUk (Patriarch) on Jul 04, 2011 at 04:20 UTC

    Like so?

    print for map{ /(\D+)?(\d+)-\1?(\d+)/ ? ( map{ ($1||'') . $_ } $2 .. $3 ) : $_ } split ';', 'ph1-3;25;t3;t47-t50;d4-6';; ph1 ph2 ph3 25 t3 t47 t48 t49 t50 d4 d5 d6

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Smart-ass...   ;-)   :-D   ;-)   :-D

      Just another demo of why Perl is called “the Swiss Army Knife® of Computer Programming.”   (And why some programmers are referred to as, “The Gods.”)

      Very slick, my friend.   Very.

Re: break-down ranges
by davido (Cardinal) on Jul 04, 2011 at 04:32 UTC

    Perl is helpful in expanding ranges automatically, even if they have alpha characters in them. The behavior of, say, 'a4' .. 'd8' follows Perl's behavior for the ++ operator. So the key is to make sure that if there are any alpha characters in the 'low', they should also exist in the 'high' end of the range. The other key is to make sure that single-component ranges get expanded to a xx .. xx form.

    use strict; use warnings; use feature qw/say/; my $string = "ph1-3;25;t3;t47-t50;d4-6"; my @output = map { s/^([[:alpha:]]+)(\d+)-(\d+)/$1$2-$1$3/; my ( $low, $high ) = split /-/; $low .. $high || $low; } split /;/, $string; say "@output";

    The output:

    ph1 ph2 ph3 25 t3 t47 t48 t49 t50 d4 d5 d6

    Which seems exactly what you were looking for.

    Update: Out of curiosity I went looking on CPAN for something that could parse ranges. Parse::Range works, and while it produces the output we want, it also generates a 'non-numeric range' warning, which seems to not be a problem. In your case it works fine, but it wouldn't be a bad idea, if you were to use this solution, to look into the source and see if the module is going to behave nice for your specific needs.

    use strict; use warnings; use feature qw/ say /; use Parse::Range qw/ parse_range /; say parse_range( $string );

    The output:

    non-numeric range: 'ph1-3;25;t3;t47-t50;d4-6' at C:/strawberry/perl/si +te/lib/Parse/Range.pm line 7. ph1 ph2 ph3 25 t3 t47 t48 t49 t50 d4 d5 d6

    As you can see, if you ignore the warning, the output is correct.


    Dave

      That is exactly what I was looking for. Thanks! Could I ask you for another favour, since I am not a programmer? The file that I need to parse holds multiple lines with a record-id followed by 4 spaces followed by the string with inventorynumbers. For example: 123 ph1-3;25;t3 234 45;67-69 345 t200 What I need to end up with is a file that looks like this: 123 ph1 123 ph2 123 ph3 123 25 123 t3 234 45 234 67 234 68 234 69 345 t200 So, I would like to know is how to write a Perl script that reads a file line by line, breaks down each line in record-id followed by inventorynumber for each inventory number in the range given for that record-id and does that for each record-id. I take it that I have to use an array for that, and voila, I am already out of my depth. If you think I am too cheeky, just say so. Eventually I will figure out how to read the contents of a file and how to use arrays. It is just that I amm pressed for time. I would never have figured out how to use Perl to break-down ranges though : )

        Teaching is free here, and I am happy with that arrangement, because it means learning here is free as well. I don't get a warm fuzzy about doing work for free though.

        Read your file in a loop. Chomp inside the loop, then capture into $1 with m/^(\d+)\s{4}/. Save what's in $1 for later. Substitute away that initial stuff so that it doesn't get in the way of your subsequent parsing. Then just prepend what you saved up front per line to the front of your output for that line after parsing the ranges.

        Before making your next post make sure to have a look at Writeup Formatting Tips. And please be sure to show us the code you've tried, and where you're stuck in the future. Welcome to the Monastery, a home for Seekers of Perl Wisdom.


        Dave