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

Dear monks,

I'm scratching my head on this string-to-number conversion. Here's the basis of the problem.

I get from a ncurses multi-box (that has 10 choices) an array of indices as to which check boxes are checked. Like this:

8 1 9

This one shows that checkboxes # 1, 8 and 9 were checked. The first checkbox of the list is # 0.

Now, I want to transfer this to two 8-bit bit-mapped numbers. The number above would yield, as a 16-bit number:

0x0302

I would like to get the following two 8-bit numbers:

0x03 0x02

Is there a way to do this without having any additional module ?

On the other hand, I tried Bit::Vector. It is a surprisingly complete module and I can do this, @sel being the array returned from the ncurses checkbox):

my $vector = Bit::Vector->new(16); $vector->Index_List_Store(@sel); $value = $vector->to_Hex();

Sure enough, $value = 0302. But it is a string, eg: 0x30333032.

How can I get my two bytes out of that ?

Moreover, am I making things more complicated that they should be ? This surely does not make me feel lazy ! ;-)

Thanks !

Al

Replies are listed 'Best First'.
Re: Making two 8-bit numbers from indices of set bits
by GrandFather (Saint) on Feb 19, 2007 at 23:21 UTC
    use strict; use warnings; my @indicies = qw(8 1 9); my $code; $code |= 1 << $_ for @indicies; printf "0x%02X 0x%02X\n", $code >> 8, $code & 0xFF;

    Prints:

    0x03 0x02

    DWIM is Perl's answer to Gödel

      $code |= 1 << $_
      That's what vec is for. It also works for indexes higher than the computer's native integers.

      vec( $code, 1, $_ ) = 1;

      ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: Making two 8-bit numbers from indices of set bits
by almut (Canon) on Feb 19, 2007 at 23:20 UTC

    Something like this should work:

    my @vals = (8, 1, 9); my $val; $val |= 2**$_ for @vals; my @bytes = unpack "CC", pack("n", $val); printf "0x%02x 0x%02x\n", @bytes;
Re: Making two 8-bit numbers from indices of set bits
by johngg (Canon) on Feb 19, 2007 at 23:30 UTC
    Perhaps this

    $ perl -le ' > vec($str, $_, 1) = 1 for 1, 8, 9; > $high = unpack q{xc}, $str; > $low = unpack q{c}, $str; > print qq{$high - $low};' 3 - 2 $

    Cheers,

    JohnGG

      Works fine. Thanks. When unselecting the only selection of the multiple choice list, ncurses will return an empty array that cannot go through, so I've added a condition:

      # (inside ncurses listbox callback) my $listbox = shift; my @sel = $listbox->id; $byte2 = 0; $byte1 = 0; if (@sel) { my $code; $code |= 1 << $_ for @sel; $byte2 = $code >> 8; $byte1 = $code & 0xFF; }

      I've taken that solution although the unpack and pack is interesting. Thanks again !

      Cheers.

      Al

Re: Making two 8-bit numbers from indices of set bits
by Anno (Deacon) on Feb 20, 2007 at 10:57 UTC
    For purposes like this I am keeping a pair of functions in a private utility library (somewhat simplified):
    sub str_from_bits { my $vec = ''; vec( $vec, $_, 1) = 1 for @_; $vec; } sub str_to_bits { my $vec = shift; grep vec( $vec, $_, 1), 0 .. 8*( length $vec); }
    str_from_bits() constructs a bit vector from a list of integers that are the indices of the one-bits in the string. str_to_bits() does the reverse. The function str_from_bits() applies directly to your problem:
    my $vec = str_from_bits( 8, 1, 9); my ( $lo, $hi) = ( vec( $vec, 0, 8), vec( $vec, 1, 8)); printf "lo: 0x%02x, hi: 0x%02x\n", $lo, $hi;

    The functions above are slightly simplified. There is also a num_from_bits() that returns an integer instead of a bit string. There would also be num_to_bits()<c>, but that is unified with <c>str_to_bits() as just to_bits() which checks its argument whether it's a string or number, similar to the bitwise boolean operators.

    Together these functions cover most needs for bit manipulation that come up.

    Anno

Re: Making two 8-bit numbers from indices of set bits
by jeanluca (Deacon) on Feb 20, 2007 at 09:50 UTC
    Dear Monks

    I have to admit I don't know mutch about bits and bytes (for me just a $var seem to work pretty well).
    So my question is why one should do all this complex stuff. Is it performance or binary socket/file IO ?

    Any suggestion about documentation about this subject ?

    Thnx
    LuCa
      The subject is about anything that works with bit fields. Many microcontrollers for instance will use bit fields for various statuses. On the Perl side these have to be packed in binary format and then sent (TCP, serial port, etc...) to the microcontroller.

      In the PC world we do not care anymore about these things since we have so much RAM. We could use 32 32-bit variables to represent 32 booleans and who would complain ? ;-)

      As for info on the subject, I guess you have to have a need to do such operations in the first place. If you want to only experiment and have fun while doing it, you can use a POE TCP server and client and exchange data using the way described here.