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

Hello PerlMonks,

From a device I get binary data, and in this data I need to replace a (binary sub-)string by another one. I thought that I could simply read the data into a string and hoped that s/$cut/$place/g would do the trick. No such luck.

Can it be done using Perl? If so: how? The length of $cut and $place are identical, the location is unknown.

Thanks

update, made a variation on my original testcode to see if I could find the index:

my $datafile=<file with binary data>; my $searchdata=<file with string to be replaced>; my ($FH,$buf, $cut); binmode($FH); open ($FH, $datafile); read($FH, $buf, 10000); chop($buf); close ($FH); open ($FH,$searchdata ); read($FH, $cut, 100); chop($cut); close($FH); my $result = index($buf,$cut); print "position: $result\n";
Update 2: made new testdata, and the code works! Thanks!

Replies are listed 'Best First'.
Re: binary search/replace
by roboticus (Chancellor) on Jan 26, 2011 at 12:51 UTC

    momo33:

    You can certainly edit binary data in perl, and there are a number of ways to do it. I'll assume you know all about the vagaries of differences in binary data structures between CPU architectures, so I won't open that particular can of worms.

    Anyway, it works for me. If you show the code, we may be able to tell you the problem. Here's how I tested it:

    $ cat binary_snr.pl #!/usr/bin/perl use strict; use warnings; my $binstr = join("", map { chr($_) } 0 .. 20); my $search_1 = join("", map { chr($_) } 7 .. 11); my $repl_1 = join("", map { 'A' } 7 .. 11); if ($binstr =~ s/$search_1/$repl_1/) { print "Fixed first one\n"; } print join(" ", map { ord($_) < 32 ? sprintf "0x%02x", ord($_) : $_ } split /|/, $binstr), "\n"; $ perl binary_snr.pl Fixed first one 0x00 0x01 0x02 0x03 0x04 0x05 0x06 A A A A A 0x0c 0x0d 0x0e 0x0f 0x10 +0x11 0x12 0x13 0x14 $

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

    Update: minor text update to "If you show" sentence.

      Thank you both.

      I should have included some code, but my code was so trivial that I thought it would not help a bit. The problem in itself was clear (I hope).

      With your directions I will try to get a working example

Re: binary search/replace
by tilly (Archbishop) on Jan 26, 2011 at 12:57 UTC
    If you call quotemeta on $cut, you should get a string that makes this work. Alternately you can use index to find the location to replace, and then use substr to do the replacement.

    If you are on Windows, you will need to use binmode before reading in the data and substring to be sure that you are correctly loading the binary data.

Re: binary search/replace
by nif (Sexton) on Jan 26, 2011 at 13:22 UTC

    You can also use something like "\xFF\x55\x74\xE7" to specify your "$cut" and "$place" strings
    In the example below "\x00\x55" is replaced by "\x22\xFF"

    #!/usr/bin/perl use strict; use warnings; sub hex2str { my $hex = shift; my @bytes = unpack('C*', $hex); join(' ', map {sprintf('%02X',$_)} @bytes); } my $bin = qq{\x34\x65\xF5\x00\x55\x34\x65\xF5\x00\x55\x77\xED}; print hex2str($bin), "\n"; $bin =~ s/\x00\x55/\x22\xFF/g; print hex2str($bin), "\n";
    Output:
    34 65 F5 00 55 34 65 F5 00 55 77 ED 34 65 F5 22 FF 34 65 F5 22 FF 77 ED

Re: binary search/replace
by bart (Canon) on Jan 26, 2011 at 13:37 UTC
    Is that literal data? If so, you might get by using index and substr.

    When using regexes, it's safest to use quotemeta — failure to do so may be the cause of why it didn't work for you:

    s/\Q$cut/$replace/;
Re: binary search/replace
by ambrus (Abbot) on Jan 26, 2011 at 13:58 UTC

    Did you try throwing in some binmodes for all the input and output files? It's easy to forget.