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

Hello, I was hoping someone could point me in the right direction/help me out with the following problem. I have the following code shown below.
#!/usr/bin/perl use strict; use warnings; my $input_string = 'test'; my $hexchars = ''; foreach my $chars (split(//,$input_string)) { $hexchars .= sprintf "%x", ord($chars); } print "$input_string\n"; print "$hexchars\n"; my $length = length $hexchars; my $start = 0; my $end = $start + $length; print "length $length\n"; print "start_offset: $start\n"; print "end_offset: $end\n"; for ( $start = 0; $start < $length; $start ++ ) { print $start, "\n"; }
What I am trying to do is process the output of the $hexchars variable but doing so 4 bytes at a time until no more data is left in the $hexchars variable. I am trying to reverse/swap two bytes from the 4 bytes that I am processing at a time. Example: the string "test" which I have in the input variable gets converted to: 74657374 in HEX. Now what I am trying to do is grab 4 bytes so in this case 7465 and swap/reverse them so they show up as so 6574. Then continue on to the next 4 characters and do the same until the variable is empty. Where I am having trouble is how to I process 4 characters at a time? Thanks for the help in advance.

Replies are listed 'Best First'.
Re: Process a set num of characters at a time
by moritz (Cardinal) on May 04, 2009 at 16:59 UTC
    You can do it like this:
    $ perl -wE 'say for unpack "(A4)*", "74657374"' 7465 7374

    But maybe there's a shorter way to do what you want with pack and unpack. See perlpacktut for a gentler introduction.

Re: Process a set num of characters at a time
by CountZero (Bishop) on May 04, 2009 at 17:17 UTC
    A simple regex-substitution will do the trick:
    s/(.{2})(.{2})/$2$1/g
    Feed it any string (such as your string of HEX-values) and it will swap positions 1+2 with 3+4, 5+6 with 7+8, ...

    Update: As moritz pointed out to me, this will not work with strings which have embedded newlines ("\n"). If you want that, add an "s" modifier to the regex.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Process a set num of characters at a time
by toolic (Bishop) on May 04, 2009 at 17:06 UTC
    Using a regular expression:
    use strict; use warnings; my $input_string = 'test'; my $hexchars = ''; foreach my $chars (split(//,$input_string)) { $hexchars .= sprintf "%x", ord($chars); } print "$input_string\n"; print "$hexchars\n"; my $length = length $hexchars; my $start = 0; my $end = $start + $length; print "length $length\n"; print "start_offset: $start\n"; print "end_offset: $end\n"; for ( $start = 0; $start < $length; $start ++ ) { print $start, "\n"; } while ($hexchars =~ /(.{4})/g) { print "$1\n"; }
Re: Process a set num of characters at a time
by jwkrahn (Abbot) on May 04, 2009 at 18:24 UTC

    Perhaps you want something like this:

    $ perl -le' my $input_string = "test"; print substr( $_, 2 ), substr $_, 0, 2 for unpack( "H*", $input_string + ) =~ /.{1,4}/g; ' 6574 7473
Re: Process a set num of characters at a time
by AnomalousMonk (Archbishop) on May 05, 2009 at 14:15 UTC
    Another approach (assumption: string is one byte per character):
    >perl -wMstrict -le "for my $in (@ARGV) { my @nuxi = map sprintf('%04x', $_), unpack 'v*', $in ; print qq{$in -> @nuxi}; } " test tester teste test -> 6574 7473 tester -> 6574 7473 7265 teste -> 6574 7473
    Note that this approach fails for odd-length strings. Depending on just what output you want, this can be fixed, e.g.:
    >perl -wMstrict -le "for my $in (@ARGV) { my @nuxi = map sprintf('%04x', $_), unpack 'v*', $in . qq{\000} ; print qq{$in -> @nuxi}; } " test tester teste test -> 6574 7473 tester -> 6574 7473 7265 teste -> 6574 7473 0065
    (This is an example of the NUXI problem.)
Re: Process a set num of characters at a time
by johngg (Canon) on May 05, 2009 at 11:18 UTC
    foreach my $chars (split(//,$input_string)) { $hexchars .= sprintf "%x", ord($chars); }

    Be aware that the format %x will cause you problems if your string contains characters with ordinals in the range 0x00 through 0x0f. Consider the following code where the string is terminated with CRLF.

    $ perl -le ' $str = qq{test\r\n}; $wrong = join q{}, map { sprintf q{%x}, ord } split m{}, $str; print $wrong; $right = join q{}, map { sprintf q{%02x}, ord } split m{}, $str; print $right;' 74657374da 746573740d0a

    You can see that %x will use a variable width depending on the value to be formatted and so, for single digit values, doesn't show a leading zero. Conversely, %02x does because it is specifying a width of 2 characters, padding with leading zeros as necessary.

    I hope this is of interest.

    Cheers,

    JohnGG