http://www.perlmonks.org?node_id=985304

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

Hi Monks.

I've done some searching but I honestly can't even come up with a good way to search for this one...and unfortunately I have no code to share because this time I haven't the slightest idea how to handle it.

I am parsing output from a program here at work. The output looks like this:

293F:2945

I need to get each hex number in that sequence...in other words what I would need from the above would be:

293F,2940,2941,2942,2943,2944,2945

Never run across a situation quite like this one and not sure what to do. Thanks for taking the time to look this over!

Replies are listed 'Best First'.
Re: Extracting full digits from a range
by bart (Canon) on Aug 03, 2012 at 18:44 UTC
    You have to
    1. get the start and end values,
      my($min, $max) = /\b([0-9a-f]+):([0-9a-f]+)\b/i;
    2. convert them to integers
      foreach($min, $max) { $_ = hex($_); }
    3. turn them into a range or span:
      my @list = $min .. $max;
    4. turn each into a hex number.
      my @hex = map { sprintf '%X', $_ } $min .. $max;
      will do.
    5. print out the list with ',' between them:
      $, = ','; print @list;
      update Oops, that should have been
      $, = ','; print @hex;

      Did you test? It outputs

      10559,10560,10561,10562,10563,10564,10565
        Yeah, I printed the wrong list.
Re: Extracting full digits from a range
by davido (Cardinal) on Aug 03, 2012 at 23:28 UTC

    The following code takes these steps:

    1. Unpack two four-character strings while discarding a character in the middle. These strings will represent two hex sequences.
    2. Pack the two hex strings.
    3. Unpack the packed string as two network-endian unsigned shorts.
    4. Generate a range from the low integral value to the high integral value.
    5. Pack all of the integral elements of that range.
    6. Unpack them as a series of hex strings.
    7. Print them.
    my $string = "293F:2945"; my ( $low, $high ) = unpack( 'nn', pack( 'H4H4', unpack( 'A4xA4', $str +ing ) ) ); print "$_ " for unpack( '(H4)*', pack( 'n*', $low .. $high ) );

    The output:

    293f 2940 2941 2942 2943 2944 2945

    Let's put it in a (debatably) well-named subroutine so we remember what this gem does:

    sub enum_hex_range_from_string { my ( $low, $high ) = unpack( 'nn', pack( 'H4H4', unpack( 'A4xA4', $string ) ) ); return unpack( '(H4)*', pack( 'n*', $low .. $high ) ); }

    Just add some camel casing randomly through the subroutine name and it would feel right at home in a PHP application.

    The hardest part of coming up with something like this is keeping track of which of the many pack/unpack template tokens to use from [Hh] and [SsNnVv]. I have to admit to needing to experiment with a couple of one-liners to make sure my conversions were going the right direction.


    Dave

Re: Extracting full digits from a range
by AnomalousMonk (Archbishop) on Aug 03, 2012 at 18:50 UTC

    One way:

    >perl -wMstrict -le "my $s = 'xxx 293F:2945 yy ff:103 zz'; ;; my @ranges = map { eval join '..', map hex, split /:/ } $s =~ m{ [[:xdigit:]]+ : [[:xdigit:]]+ }xmsg ; ;; printf q{%x }, $_ for @ranges; " 293f 2940 2941 2942 2943 2944 2945 ff 100 101 102 103
Re: Extracting full digits from a range
by BrowserUk (Patriarch) on Aug 04, 2012 at 00:39 UTC

    '293F:2945' =~ /([0-9a-f]+):([0-9a-f]+)$/i and @range = map sprintf( '%x',$_ ), eval "0x$1 .. 0x$2";; print @range;; 293f 2940 2941 2942 2943 2944 2945

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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.

    The start of some sanity?

      Thanks for the help everyone. This one looks like it will fit best into my script, however I'm having one more issue, and it's no doubt something really ridiculous I'm doing wrong.

      I have the following code snippet:

      #!c:/perl/bin/perl use strict; open (HYPERS, "< hypers.txt"); my @hypers = <HYPERS>; chomp @hypers; print "Hypers array: @hypers\n"; foreach my $line(@hypers){ $line =~ /([0-9a-f]+):([0-9a-f]+)$/i and my @range = map sprintf( '%x,',$_ ), eval "0x$1 .. 0x$2";; print @range;; } Output: Hypers array: 293F:2946

      So I know the array is being read, but when trying to shove it into that regex it's coming out as '', almost like it's not there.

      The HYPERS file looks like this:

      293F:2946
        it's no doubt something really ridiculous I'm doing wrong.

        Dunno! Your code and your hypers file produce the apparently desired result on my machine:

        C:\test>type junk12.pl #!c:/perl/bin/perl use strict; open (HYPERS, "< hypers.txt"); my @hypers = <HYPERS>; chomp @hypers; print "Hypers array: @hypers\n"; foreach my $line(@hypers){ $line =~ /([0-9a-f]+):([0-9a-f]+)$/i and my @range = map sprintf( '%x,',$_ ), eval "0x$1 .. 0x$2";; print @range;; } C:\test>type hypers.txt 293F:2946 C:\test>junk12 Hypers array: 293F:2946 293f,2940,2941,2942,2943,2944,2945,2946, C:\test>

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        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.

        The start of some sanity?

        Works for me. Is there any whitespace in the input file (a space at the end of the line)?
        You migh also suffer from buffering: print a newline after printing the range.
Re: Extracting full digits from a range
by Kenosis (Priest) on Aug 03, 2012 at 19:56 UTC

    And another option:

    use Modern::Perl; '293F:2945' =~ /(.+):(.+)/ and say join ',', map { sprintf '%X', $_ } eval "0x$1..0x$2";

    Output

    293F,2940,2941,2942,2943,2944,2945
Re: Extracting full digits from a range
by johngg (Canon) on Aug 03, 2012 at 22:02 UTC

    Another variation using an on-the-fly subroutine.

    knoppix@Microknoppix:~$ perl -Mstrict -Mwarnings -E ' > say join q{,}, > map { sprintf q{%X}, $_ } > sub { hex $_[ 0 ] .. hex $_[ 1 ] }-> > ( split m{:}, q{293F:2945} );' 293F,2940,2941,2942,2943,2944,2945 knoppix@Microknoppix:~$

    Cheers,

    JohnGG

A reply falls below the community's threshold of quality. You may see it by logging in.