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

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

hello again :D

seems i am in another pickle, i have a unpack function that read a binary file, then unpacks it in its hexadecimal representaion. i am successfully able to match 80-89 and 8000 - 8900, but the problem is i need to match 8A-8F, and also A0. here is the example code:
open ( my $flash, '<', $dropfile ); open ( my $reversedFlash, '<', "$dir/file"); my $tid; my $buf; my $test = "TEST SYSTEM"; my $tool = "REFERENCE TOOL"; my $db = "DEBUG SYSTEM"; my $arc = "ARCADE SYSTEM"; my $jp = "Japan"; my $usa = "United States"; my $eu = "Europe"; my $kor = "Korea"; my $uk = "United Kingdom"; my $mex = "Mexico"; my $au = "Australia"; my $sa = "South Asia"; my $tw = "Taiwan"; my $rs = "Russia"; my $ch = "China"; my $hk = "Hong Kong"; my $bz = "Brazil"; seek ($flash, 0x2F074, 0); read ($flash, my $temp, 0x02); my $data = unpack('H*', $temp); print $data; #prints correct value always if ($data == "8000"){ $tid = "TEST"; $buf = $test; } if ($data == "0080"){ $tid = "TEST"; $buf = $test; } if ($data == "8100"){ $tid = "TOOL"; $buf = $tool; } if ($data == "0081"){ $tid = "TOOL"; $buf = $tool; } if ($data == "8200"){ $tid = "DEBUG"; $buf = $db; } if ($data == "0082"){ $tid = "DEBUG"; $buf = $db; } if ($data == "8300"){ $tid = "CEX"; $buf = $jp; } if ($data == "0083"){ $tid = "CEX"; $buf = $jp; } if ($data == "8400"){ $tid = "CEX"; $buf = $usa; } if ($data == "0084"){ $tid = "CEX"; $buf = $usa; } if ($data == "8500"){ $tid = "CEX"; $buf = $eu; } if ($data == "0085"){ $tid = "CEX"; $buf = $eu; } if ($data == "8600"){ $tid = "CEX"; $buf = $kor; } if ($data == "0086"){ $tid = "CEX"; $buf = $kor; } if ($data == "8700"){ $tid = "CEX"; $buf = $uk; } if ($data == "0087"){ $tid = "CEX"; $buf = $uk; } if ($data == "8800"){ $tid = "CEX"; $buf = $mx; } if ($data == "0088"){ $tid = "CEX"; $buf = $mx; } if ($data == "8900"){ $tid = "CEX"; $buf = $au; } if ($data == "0089"){ $tid = "CEX"; $buf = $au; } if ($data =~ "008A"){ #from this point it doesnt match $tid = "CEX"; $buf = $sa; } if ($data =~ "8A00"){ $tid = "CEX"; $buf = $sa; } if ($data == "8B00"){ $tid = "CEX"; $buf = $tw; } if ($data == "008B"){ $tid = "CEX"; $buf = $tw; } if ($data == "8C00"){ $tid = "CEX"; $buf = $rs; } if ($data == "008C"){ $tid = "CEX"; $buf = $rs; } if ($data == "8D00"){ $tid = "CEX"; $buf = $ch; } if ($data == "008D"){ $tid = "CEX"; $buf = $ch; } if ($data == "8E00"){ $tid = "CEX"; $buf = $hk; } if ($data == "008E"){ $tid = "CEX"; $buf = $hk; } if ($data == "8F00"){ $tid = "CEX"; $buf = $bz; } if ($data == "008F"){ $tid = "CEX"; $buf = $bz; } if ($data == "A000"){ $tid = "ARC"; $buf = $arc; } if ($data == "00A0"){ $tid = "ARC"; $buf = $arc; } $lb3->Append($tid) ; $lb4->Append($buf) ;


I know that this is probably simple but i have tried many different ways including:
($data =~ "xxxx"); and ($data eq "xxxx") and ($data == "xxxx" || "xxxx") tried with single quotes ect and many other things
and i cannot get it to match 008A - 008F or 00A0 || 8A00 - "8F00" or "A000". the reason for 8100 and 0080 ect ect, is the endianess of what i am reading.

Also i want to add, that once i am done with this tool (that i have been working on forever xD) i will share it with everyone and i will post a file to use with it and the complete source code as well, that way yall can see what you have helped me work thru :)
and thanks again sirs/ma'am's

Replies are listed 'Best First'.
Re: perl unpack and matching the unpacked data
by Corion (Patriarch) on Jul 17, 2014 at 17:28 UTC

    If you run your program with warnings enabled, Perl will tell you that 00A0 (for example) does not look like a number, but is used with the numeric compare ("==") operator. Most likely you want to use the string compare operator, eq.

      I noticed that you are using a regular expression

      if ($data =~ "008A")

      I agree with james28909 says you need to use a eq

Re: perl unpack and matching the unpacked data
by Laurent_R (Canon) on Jul 17, 2014 at 18:05 UTC
    Corion has most probably given you the solution, but there are various ways your code could be shorter and possibly faster (if that matters). At the very least you could have:
    if ($data eq "8000" or $data eq 0080){ $tid = "TEST"; $buf = $test; } elsif ($data eq "8100" or $data eq "0081"){ $tid = "TOOL"; $buf = $tool; } elsif ...
    This would make your code twice shorter and probably a bit faster. You could also notice that $tid is "CEX" in most of the cases, so you could reorganize part of your tests:
    $tid = "CEX" if grep {$_ eq $data} qw /8300 0083 ... 8F00 008F/;
    There are other possible shortcuts, but using too many of them might lead to less clear code. But I think you should think of some basic ones at least. You could also "normalize" $data before testing, i.e. if $data is "00xx", change it to "xx00" and do your tests afterwards, that would also reduce your code by about half.
      isnt something like:
      $data eq "8000" or $data eq "0080" the same as: $data eq "8000" || $data eq "0080";
      i thought i read somewhere that its was better to use "||" instead of "or" and use "&&" instead of "and". anyways...like i said i have tried alot of different avenues, i have even tried only swapping the 8A-8F/A0 with eq to no avail.
      i have been up for a while though so maybe i will take a break from it for a while and get some rest and come back to it with a fresh mind lol. I am looking forward to sharing the tool and the code to see any criticism or input on how to make it better, because honestly this is my first ever program ive done that actually amounted to anything, and its going to have a gui :D
      everything else works great except for this snippet lol. but ill take a break and come back later.
      also thanks for the input :)

        || and or (and, similarly, && and and) have the same semantics in principle, but the former has a higher precedence than the latter. eq has a higher precedence than either, so it doesn't make any difference which one you use there, but it can be important in other contexts.

        Operator Precedence and Associativity has more, as does chapter 3 of Programming Perl.

        isnt something like:
        $data eq "8000" or $data eq "0080" the same as: $data eq "8000" || $data eq "0080";

        A little experimentation should reveal that Perl thinks it is:

        c:\@Work\Perl>perl -wMstrict -MO=Deparse,-p -le "my $data = '8000'; ;; if ($data eq '8000' or $data eq '0080') { print 'yes 1'; }; if ($data eq '8000' || $data eq '0080') { print 'yes 2'; }; " BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } use strict 'refs'; (my $data = '8000'); if ((($data eq '8000') or ($data eq '0080'))) { print('yes 1'); } if ((($data eq '8000') or ($data eq '0080'))) { print('yes 2'); } -e syntax OK

        ... i have ... tried ... swapping the 8A-8F/A0 with eq to no avail.

        Again with the experimentation:

        c:\@Work\Perl>perl -wMstrict -le "my $data = '8000'; ;; if ($data eq '8000' or $data eq '0080') { print 'yes 1'; }; if ($data eq '8000' || $data eq '0080') { print 'yes 2'; }; " yes 1 yes 2
        I don't know what code you tried when you used  eq (the proper comparator for strings), but clearly it should work. At the same time, I would also agree with other respondents that a re-organization of the code to avoid an eye- and mind-bezoggling onslaught of if-statements would be very valuable just for the great increase in readability and maintainability I would expect it to yield.

        i thought i read somewhere that its was better to use "||" instead of "or" and use "&&" instead of "and".

        The problem with  and and  or is that these logical operators have such low precedence: their precedence is lower than  , (comma), i.e., lower than whale dung. This was done so that a statement like
            open my $fh, '<', $filename or die "opening '$filename': $!";
        would work as expected. Unfortunately, such low precedence has some surprising side-effects, hence  && and  || are recommended for 'normal' usage, a recommendation I would endorse since I've stumbled over these odd side-effects myself.
        Update: Here's an example of a precedence-induced difference between  ! and  not. This code produces warnings, but you may not be so lucky.

        c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my ($x, $y) = (4, 5); ;; my @ra = (1, 2, ! 3, $x, $y); dd \@ra; ;; my @rb = (1, 2, not 3, $x, $y); dd \@rb; ;; print qq{but \$x and \$y are still $x and $y}; " Useless use of a constant (3) in void context at -e line 1. Useless use of private variable in void context at -e line 1. [1, 2, "", 4, 5] [1, 2, ""] but $x and $y are still 4 and 5

Re: perl unpack and matching the unpacked data
by james28909 (Deacon) on Jul 18, 2014 at 00:34 UTC
    Final working code:
    my $tid; my $buf; my $test = "TEST SYSTEM"; my $tool = "REFERENCE TOOL"; my $db = "DEBUG SYSTEM"; my $arc = "ARCADE SYSTEM"; my $jp = "Japan"; my $usa = "United States"; my $eu = "Europe"; my $kor = "Korea"; my $uk = "United Kingdom"; my $mex = "Mexico"; my $au = "Australia"; my $sa = "South Asia"; my $tw = "Taiwan"; my $rs = "Russia"; my $ch = "China"; my $hk = "Hong Kong"; my $bz = "Brazil"; seek ($flash, 0x2F074, 0); read ($flash, my $temp, 0x02); my $dat = unpack('H*', $temp); my $data = uc$dat; if ($data eq "8000" || $data eq "0080"){ $tid = "TEST"; $buf = $test; } elsif ($data eq "8100" || $data eq "0081"){ $tid = "TOOL"; $buf = $tool; } elsif ($data eq "8200" || $data eq "0082"){ $tid = "DEX"; $buf = $db; } elsif ($data eq "8300" || $data eq "0083"){ $tid = "CEX"; $buf = $jp; } elsif ($data eq "8400" || $data eq "0084"){ $tid = "CEX"; $buf = $usa; } elsif ($data eq "8500" || $data eq "0085"){ $tid = "CEX"; $buf = $eu; } elsif ($data eq "8600" || $data eq "0086"){ $tid = "CEX"; $buf = $kor; } elsif ($data eq "8700" || $data eq "0087"){ $tid = "CEX"; $buf = $uk; } elsif ($data eq "8800" || $data eq "0088"){ $tid = "CEX"; $buf = $mex; } elsif ($data eq "8900" || $data eq "0089"){ $tid = "CEX"; $buf = $au; } elsif ($data eq "8A00" || $data eq "008A"){ $tid = "CEX"; $buf = $sa; } elsif ($data eq "8B00" || $data eq "008B"){ $tid = "CEX"; $buf = $tw; } elsif ($data eq "8C00" || $data eq "008C"){ $tid = "CEX"; $buf = $rs; } elsif ($data eq "8D00" || $data eq "008D"){ $tid = "CEX"; $buf = $ch; } elsif ($data eq "8E00" || $data eq "008E"){ $tid = "CEX"; $buf = $hk; } elsif ($data eq "8F00" || $data eq "008F"){ $tid = "CEX"; $buf = $bz; } elsif ($data eq "A000" || $data eq "00A0"){ $tid = "ARC"; $buf = $arc; } else { $tid = "UNKNOWN"; $buf = "UNKNOWN"; } $lb3->Append("$tid") ; $lb4->Append("$buf") ;
    funny thing is, this also wasnt working either ;0
    what i believe was messing me up was the capitalization, because it wasnt working until i uppercased the output from unpack 'H*'

    anyways this is working flawless :D
    thanks for steering me in the right direction because this saved me from having twice as many lines :)
    EDIT: now 500 dollars who can do this in a one liner :P

      You may be able to save yourself some more typing. This doesn't properly handle your  "UNKNOWN" case, but I leave that to you to fix if so inclined.

        that wasnt a one liner :P but yes yes, me likey :) ive never tried to use hashes, and i believe in this particular instance it would probably be optimum, that way a person could access that anywhere in the code, and have something more legible and easier to update if needed.