I wanted to show a variable-length value as a series of hex bytes. I wavered a moment between what I did (map each byte) and unpacking the whole thing as a hex string and then inserting spaces into that string.

Which is faster, more efficient, or otherwise better?

Hey, which is shorter!

sub hexout { join (' ', map { sprintf "%02x", $_ } (unpack ("C*", shift))); }

Replies are listed 'Best First'.
Re: golf anyone? (hexdump)
by MrNobo1024 (Hermit) on Aug 09, 2002 at 03:35 UTC
    16 bytes: (not exact right output format) printf'%v2X',pop 34 bytes: $_=sprintf'%v2X',pop;y/. / 0/;print

    --MrNobo1024
    s]]HrLfbfe|EbBibmv]e|s}w}ciZx^RYhL}e^print

      Nothing like an opportunity to improve:
      sprintf'%*vX',$",@_
      That's 19 characters by my count. Using the internal equivalent of space, plus why pop when you can just fire it in and hope for the best?

      Am I correct in assuming the function has to return the string instead of printing it? One character either way.

      I also think this only works in Perl 5.6 or better. A test in 5.005_02 fails, printing %*vX In that case, I think a 33 character solution is:
      $_=unpack"H*",pop;s/..\B/$& /g;$_
      As a note, I would have used @_ in the second one, but under an older version, it would seem that the unpack function is prototyped and this array is rendered to hex 31, which is in fact an ASCII "1". This could save one stroke, for a total of 32.

      I do like jmcnamara's innovative solution.

      Nice MrNobo. You can reach a compromise in length between your two approaches using:

      printf '%*vX', ' ', pop;

        Wow, thanks guys. I learned something useful here.

        I knew about %v, or thought I did: I didn't realize it was a modifier, or that I could change the dot to the space!

        So (reading the perlfunc docs) what is the meaning of the V modifier? I don't understand the explaination "interpret integer as Perl's standard integer type"

        —John

Re: golf anyone? (hexdump)
by djantzen (Priest) on Aug 09, 2002 at 03:18 UTC

    $_ = unpack("h*", shift); s/([0-9a-f]{2})/$1 /g; print;

    Simpler, if not much shorter ; )

    Update: Hmm, this'll work too:

    printf("%02x ", $_) for (unpack ("C*", shift));

    2nd Update: I think I'll be trading in my extra whitespaces for a 'chop;'

Re: golf anyone? (hexdump)
by jsprat (Curate) on Aug 09, 2002 at 04:00 UTC
    Two versions:

    sub hexout { #23456789012345678901234567890123 - 33 char $_=unpack"H*",pop;s/(..)/$1 /g;$_ }

    but it appends a space at the end

    sub hexout { #234567890123456789012 - 22 sprintf'%*v2X',' ',pop }

    #2 was with a little help from the sprintf docs

    Update: Spent a little too much time. MrNobo1024 beat me to the punch ;-)

Re: golf anyone? (hexdump)
by jmcnamara (Monsignor) on Aug 09, 2002 at 08:22 UTC

    30 chars:
    sub hexout4 { "@{[unpack('H*',pop)=~/../g]}" }
    On a related note does anyone know why the sprintf format "%#x" behaves differently for 0 than for other numbers.
    printf "%#x\n", 0; printf "%#x\n", 1; __END__ Prints: 0 0x1

    I guess that the behaviour comes from the C compiler that builds perl because the following gives the same results (at least where I could test it):

    int main () { printf("%#x\n", 0); printf("%#x\n", 1); return 0; }

    So why is 0 treated differently?

    --
    John.

      Perl implements its own printf formatting. But if it follows the ANSI/ISO C specification for what %#X does, it is per spec: any non-zero output is prefixed with 0x.

      To always have the prifix, just mention it in the string: "0x%X".

      I like your idea of using interpolation instead of join.

      —John