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

SPEC: Read 1376 byte binary file into uint16_t array overwrite item[16] with dec<7360> = 0X1cc0 overwrite item[22] with dec<4912> = 0X1330 Write file to disk sub prefix_hdr() { $hsize=1376; # Size of 16-bit grayscale tif header $hfn='d:/pic/misc/gray.3689x2462.hdr'; # Md5 ~29992 $brfn='6s-254550.7360x4912.blue.raw'; # photoshops $rsize=-s $brfn; $mutant='blue.7360x4912.tif'; open(H, "<$hfn"); binmode H; $bread=read(H, $hdr, $hsize); close H; printf("Read $bread B from $hfn\n"); @rgb=unpack("S*", $hdr); # RAW RGB UShort array printf("RGB[16]=%hu, [22]=%hu\n", $rgb[16], $rgb[22]); $rgb[16]=pack("S", 7360); # Overwrite 3689 with 7360 $rgb[22]=pack("S", 4912); # 2462 -> 4912 printf("FIXED? RGB[16]=0X%04hu, [22]=%hu\n", $rgb[16], $rgb[22]); } Read 1376 B from d:/pic/misc/gray.1376b.3689x2462.hdr RGB[16]=3689, [22]=2462 << PERFECT! Argument "&#9492;^\" isn't numeric in printf Argument "0^S" isn't numeric in printf FIXED? RGB[16]=0X0000, [22]=0 <<<<<<WRONG ----------------------------------------------------

File reads fine. Unpacks perfectly and the numbers print

Pack is not packing the unsigned short:

pack(TEMPLATE,LIST) S An unsigned short value. Template="S" List = one value, DECIMAL 7360

How does one do this in Perl

int xres=7360; uint16_t my_ui16[688]; ... my_ui16[16] = (uint16_t)xres; printf("Xres=%hu\n"); >> Xres=7360
============================================================

>> open my($outfh),'>:raw', 'stuff';

Opening the file, put it in BINMODE and reading into a blob worked.

Unpacking the binary blob into an intelligible unsigned int array also works.

But, writing does NOT work in BINmode? Reading didn't need ':raw<~!%_'

>> print $outfh pack 'S<*', @rgb; We skipped the part about overwriting the default values.

How is it possible to take a proven correct UINT16 array

read in Perl and overwrite 2 values? Generating a real, uint16 from scratch was the

obstacle. The PACK and UNPACK do not appear to do inverse operations.

Applying PACK() as documented with an 'S' produced unprintable junk.

The original array values printed perfectly as %uh, unsigned shorts.

How does one translate this C into perl?: </p

my_uint_16_var = (uint16_t) 7360;

It has to be a drop in replacement for the default value, the >>3689<< stock value, not ["&#9492;^\"] << ??

:RAW???

Those layers will also be ignored if you specifying a colon with no na +me following it. In that case the default layer for the operating sys +tem (:raw on Unix, :crlf on Windows) is used.

Binmode does away with any cr/lf funkiness.

regardless of platform, use binmode() on binary data, like images, for + example.
I am on Windoz, not *nix. Do I need :CRLF??? This is an OBSCURE Caveat!

Replies are listed 'Best First'.
Re: Write 2 uint16_t numbers to file in Perl
by Anonymous Monk on Jan 05, 2016 at 08:36 UTC

    That is hard to read and interpret, but basically

    use autodie ; open my($outfh),'>:raw', 'stuff'; print $outfh pack 'S<*', @rgb;

    @rgb only contains numbers, when you write them to file you do the packing

    Also don't use printf as a replacement for print

      What does this mean?
      Also don't use printf as a replacement for print

      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". I knew I was on the right track :)
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Possibly an allusion to the closing sentence in the documentation of the printf function?

        Don't fall into the trap of using a printf when a simple print would do. The print is more efficient and less error prone.

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        This solution works PERFECTLY as documented

        // Grab a standard TIF hdr // Update the ushort XY res values, X@[16] and Y@[22] // Slam it on top of a UINT16 RAW and call it a TIF!!! // What could POSSIBLY be easier??? // // gcc -Ofast -ffast-math -m64 -march=sandybridge -msse4.2 -mavx -fun +roll-loops -fopenmp -flto -lm c:/bin/merge.hdr.raw.c -o c:/bin/merge +.hdr.raw.exe #include "bpbfct.c" void main(int argc, char *argv[]) { FILE *hi, *ri, *to; // File pointers for 2 ins and 1 outs struct stat statbuf; // STAT function buffer char hdrfn[255] = { 0 }; char rawfn[255] = { 0 }; char tiffn[] = "hdr.raw.merge.tif"; int hsize=1376, rsize, bread, brote, tsize; // Header and RAW sizes uint16_t *hdra; // HDR data array uint16_t *rawa; // RAW data array strcpy(hdrfn, argv[1]); strcpy(rawfn, argv[2]); hdra=(uint16_t *)malloc(hsize); hi=fopen(hdrfn, "rb"); bread=fread((void *)hdra, 1, hsize, hi); printf("Read %d Bytes from %s\n", bread, hdrfn); fclose(hi); stat(rawfn, &statbuf); // Stat the file to get its size rsize=statbuf.st_size; // File size in bytes for .raw (RGB?) file rawa=(uint16_t *)malloc(rsize); ri=fopen(rawfn, "rb"); bread=fread((void *)rawa, 1, rsize, ri); printf("Read %.6lf MBytes from %s\n", bread*1E-6, rawfn); fclose(ri); // HERE's the HARD PART!!! printf("Xres @ Hdr[16]=%hu, Yres @ [22] = %hu\n", hdra[16], hdra[2 +2]); hdra[16]=(uint16_t) 7360; <<<< Is this POSSIBLE in PERL?????? hdra[22]=(uint16_t) 4912; // Did it float?? printf("FIXED?? Xres @ Raw[16]=%hu, Yres [22] = %hu\n", hdra[16], hdra[22]); to=fopen(tiffn, "wb"); // TIF output file brote = fwrite((void *)(hdra), 1, hsize, to); brote += fwrite((void *)(rawa), 1, rsize, to); fflush(to); fflush(to); fclose(to); stat(tiffn, &statbuf); // Stat the file to get its size tsize=statbuf.st_size; // File size in bytes for .raw (RGB?) file printf("Wrote %.6lf MBytes to TIF %s, Size: T-R-H=%d\n", tsize*1E-6, tiffn, brote - rsize - hsize); free(hdra); free(rawa); } // End Main().
        I:\exp\6s-254623> merge.hdr.raw.exe gray.1376b.3689x2462.hdr 6s-2015 +.1031-254550.7360x4912.blue.cv.t.raw Read 1376 Bytes from gray.1376b.3689x2462.hdr Read 72.304640 MBytes from 6s-2015.1031-254550.7360x4912.blue.cv.t.raw Xres @ Hdr[16]=3689, Yres @ [22] = 2462 FIXED?? Xres @ Raw[16]=7360, Yres [22] = 4912 Wrote 72.306016 MBytes to TIF hdr.raw.merge.tif, Size: T-R-H=0 I:\exp\6s-254623>identify hdr.raw.merge.tif hdr.raw.merge.tif TIFF 7360x4912 7360x4912+0+0 16-bit Grayscale Gray 7 +2.31MB ... <p> Note the original and overwritten values. </p> <code> Xres @ Hdr[16]=3689, Yres @ [22] = 2462 FIXED?? Xres @ Raw[16]=7360, Yres [22] = 4912 <<< WORKS!!!

        It's Alive!

        </code>

        What does this mean?

        This part use print  printf("Read $bread B from $hfn\n");

Re: Write 2 uint16_t numbers to file in Perl
by AnomalousMonk (Archbishop) on Jan 05, 2016 at 17:49 UTC

    The critical point to remember is that
        $rgb[16]=pack("S", 7360);  # Overwrite 3689 with 7360
    does not assign 7360 to  $rgb[16] but instead assigns a two-byte string that is the S-packed representation of 7360. After the pair of assignments in your OPed code,  @rgb contains a Hellish mix of numbers and strings. Don't do that. Instead, just assign numbers to the array elements and re-pack:

    c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my @ra = (1, 3689, 2, 2462, 3); dd \@ra; ;; my $pS1 = pack 'S*', @ra; dd $pS1; print 'length $pS1 == ', length $pS1; ;; my @rb = unpack 'S*', $pS1; dd \@rb; ;; $rb[1] = 7360; $rb[3] = 4912; my $pS2 = pack 'S*', @rb; dd $pS2; print 'length $pS2 == ', length $pS2; ;; my @rc = unpack 'S*', $pS2; dd \@rc; " [1, 3689, 2, 2462, 3] "\1\0i\16\2\0\x9E\t\3\0" length $pS1 == 10 [1, 3689, 2, 2462, 3] "\1\0\xC0\34\2\x000\23\3\0" length $pS2 == 10 [1, 7360, 2, 4912, 3]


    Give a man a fish:  <%-{-{-{-<

Re: Write 2 uint16_t numbers to file in Perl
by Myrddin Wyllt (Hermit) on Jan 05, 2016 at 17:57 UTC

    You are unpacking a binary representation of a bunch of unsigned shorts ($hdr) into an array of perl scalars (@rgb), then replacing two elements in that array with packed unsigned shorts, which isn't what you want. Just substitute the new values directly into the array:

    $rgb[16] = 7360; # Overwrite 3689 with 7360 $rgb[22] = 4912; # 2462 -> 4912

    Now if you want to write this scalar array back to a binary file, try:

    open(my $outfile, '>:raw', $hfn) or die "Couldn't open $hfn: $!\n"; # Overwrites existing file print $outfile pack("S*", @rgb); close($outfile) or die "Couldn't close $hfn: $!\n";

    (The :raw modifier on the second argument of open is equivalent to using binmode).

    If you know beforehand the positions and values in the binary file you need to change, you can save yourself the trouble of reading it all in to memory, unpacking everything to an array and re-packing it all at the end by just editing it in place:

    open(my $fh, '+<:raw', $hfn) or die "Couldn't open $hfn: $!\n"; seek($fh, 2*16, 0); # Double up 'cause 'seek' wants bytes not short +ints print $fh pack('S', 7360); seek($fh, 2*22, 0); print $fh pack('S', 4912); close($fh) or die "Couldn't close $hfn: $!\n";
      seek($fh, 2*16, 0); # Double up 'cause 'seek' wants bytes not short ints

      Rather than having numeric literals running around all over the place, it's possible to capture pack-spec widths:

      c:\@Work\Perl>perl -wMstrict -le "use constant SIZEOF_S => length pack 'x[S]'; print SIZEOF_S; ;; use constant SIZEOF_WHATEVER => length pack 'x[NSc]'; print SIZEOF_WHATEVER; " 2 7
      (It may be perfectly safe to use the shorter  'S' etc., rather than the more verbose  'x[S]' form in the pack statement, but I have the sneaking suspicion that a pitfall may lurk there... can't remember it atm.)


      Give a man a fish:  <%-{-{-{-<

        The OP needs to match his pack/unpack template to the data, not the other way around. If anything, he might want to consider 'n' vs 'v' vs 'S'.

Re: Write 2 uint16_t numbers to file in Perl
by Anonymous Monk on Jan 05, 2016 at 10:36 UTC

    Can you post the output of

    use Data::Dump qw/ dd /; use Path::Tiny qw/ path /; dd( path('1376bytefile')->slurp_raw );