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

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

I have a binary file and I was able to unpack it like this:
my @lines = unpack("v*",<MYINFILE>); my $line; foreach $line (@lines) { print MYOUTFILE sprintf("%lx",$line), "\n"; }
That seems to work great, however I am unable to repack it to a bin file after I'm done with it. My goal is to be able to change a value in a binary file and then rewrite the binary file. Does anyone have any idea how to do that?

Replies are listed 'Best First'.
Re: pack/unpack binary file
by johngg (Canon) on Nov 29, 2012 at 23:07 UTC

    Having unpacked your binary file file I not sure why you don't just pack it again. Also, if you are trying to rewrite a binary file, why do you add newlines when you print?

    $ perl -e 'print pack q{v*}, 0 .. 31;' > spw1006361.bin $ ls -l spw1006361.bin -rw-rw-r--. 1 johngg johngg 64 Nov 29 22:43 spw1006361.bin $ hexdump -C spw1006361.bin 00000000 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 |......... +.......| 00000010 08 00 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 |......... +.......| 00000020 10 00 11 00 12 00 13 00 14 00 15 00 16 00 17 00 |......... +.......| 00000030 18 00 19 00 1a 00 1b 00 1c 00 1d 00 1e 00 1f 00 |......... +.......| 00000040 $ perl -Mstrict -Mwarnings -E ' > my @vals = do { > local $/; > open my $binFH, q{<}, q{spw1006361.bin} or die $!; > unpack q{v*}, <$binFH>; > }; > $_ *= 2 for @vals; > open my $binOutFH, q{>}, q{spw1006361.bin} or die $!; > print $binOutFH pack q{v*}, @vals; > close $binOutFH or die $!;' $ hexdump -C spw1006361.bin 00000000 00 00 02 00 04 00 06 00 08 00 0a 00 0c 00 0e 00 |......... +.......| 00000010 10 00 12 00 14 00 16 00 18 00 1a 00 1c 00 1e 00 |......... +.......| 00000020 20 00 22 00 24 00 26 00 28 00 2a 00 2c 00 2e 00 | .".$.&.( +.*.,...| 00000030 30 00 32 00 34 00 36 00 38 00 3a 00 3c 00 3e 00 |0.2.4.6.8 +.:.<.>.| 00000040 $

    Note how the hexdump output shows the values 0 through 31 in each little-endian short of the file before modification and 0 through 62 after modification.

    I hope this is helpful but perhaps I've misunderstood your requirement.

    Cheers,

    JohnGG

      The reason I add newlines is so I can identify a certain element by where it is in the file and modify it. I'm more than likely making this harder than it needs to be due to inexperience. My current strategy is to unpack the bin file to a text file, select a value based on location, modify that value, then repack the text file. I believe I can do that by putting the output file in bin mode and then simply doing a pack, based on what I've read in this and other threads.
        so I can identify a certain element by where it is in the file and modify it

        If you know where the element is you will know its offset into the binary file. I think sysopen, sysseek, sysread and syswrite might be what you want. You are dealing with Vax shorts so your record length is two bytes. Numbering records from zero means that to get to a certain record you sysseek to a position in the file two times its record number. Then you can either sysread two bytes and unpack them to get the value or pack a new value and syswrite the packed two byte string.

        Starting with the same binary file as before

        $ hexdump -C spw1006361.bin 00000000 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 |......... +.......| 00000010 08 00 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 |......... +.......| 00000020 10 00 11 00 12 00 13 00 14 00 15 00 16 00 17 00 |......... +.......| 00000030 18 00 19 00 1a 00 1b 00 1c 00 1d 00 1e 00 1f 00 |......... +.......| 00000040 $

        Running this script

        use strict; use warnings; use 5.014; use Fcntl qw{ :DEFAULT :seek }; use constant RECLEN => 2; my $binFile = q{spw1006361.bin}; sysopen my $binFH, $binFile, O_RDWR or die qq{sysopen: $binFile: $!\n}; my $origValue = readValue( $binFH, 7 ); say qq{Record 7 originally: $origValue}; writeValue( $binFH, 7, $origValue * 3 ); say qq{Record 7 now : @{ [ readValue( $binFH, 7 ) ] }}; close $binFH or die qq{sysopen: $binFile: $!\n}; sub readValue { my( $fh, $recNo ) = @_; sysseek $fh, $recNo * RECLEN, SEEK_SET or die qq{sysseek: $!\n}; my $bytesRead = sysread $fh, my $buffer, RECLEN; if ( not defined $bytesRead ) { die qq{sysread: $!\n}; } elsif ( $bytesRead == 0 ) { die qq{sysread: EOF\n}; } elsif ( $bytesRead != RECLEN ) { die qq{sysread: expected @{ [ RECLEN ] } bytes,}, qq{ got $bytesRead\n}; } else { return unpack q{v}, $buffer; } } sub writeValue { my( $fh, $recNo, $value ) = @_; sysseek $fh, $recNo * RECLEN, SEEK_SET or die qq{sysseek: $!\n}; my $bytesWritten = syswrite $fh, pack q{v}, $value; if ( not defined $bytesWritten ) { die qq{syswrite: $!\n}; } elsif ( $bytesWritten != RECLEN ) { die qq{syswrite: expected @{ [ RECLEN ] } bytes,}, qq{ wrote $bytesWritten\n}; } else { return; } }

        Produces this output

        ]$ ./spw1006361 Record 7 originally: 7 Record 7 now : 21 $

        The binary file now looks like this

        ]$ hexdump -C spw1006361.bin 00000000 00 00 01 00 02 00 03 00 04 00 05 00 06 00 15 00 |......... +.......| 00000010 08 00 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 |......... +.......| 00000020 10 00 11 00 12 00 13 00 14 00 15 00 16 00 17 00 |......... +.......| 00000030 18 00 19 00 1a 00 1b 00 1c 00 1d 00 1e 00 1f 00 |......... +.......| 00000040 $

        I hope this is heading in the right direction for you. Things will get more complicated if you want to delete records from the file or insert new ones but this functionality would not be too difficult to achieve.

        Cheers,

        JohnGG

Re: pack/unpack binary file
by BrowserUk (Patriarch) on Nov 29, 2012 at 22:58 UTC
    I am unable to repack it to a bin file after I'm done with it

    You aren't packing the output. You are sprintfing it to hex; which is just a text representation.

    If you want the output (re)packed, use pack. (And don't forget to binmod your output file.)


    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.

    RIP Neil Armstrong

Re: pack/unpack binary file
by Anonymous Monk on Nov 29, 2012 at 22:16 UTC

    You should show your pack code, or it will be very hard to help see what's wrong with it.

    Make sure you're using binmode too.