http://qs321.pair.com?node_id=11107879

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

Hi,

When I print a variable to STDOUT:
printf "%s.\n", $var;
I get:
QeTEv2804.

When I print the same varible to a file:
open DEBUG, ">>debug.txt" or confess "Couldn't open file";
printf DEBUG "%s.\n", $var;
close DEBUG;
I get:
QeTEv2804^@^@^@.

What could be the reason for that?
Thanks,
Christian
  • Comment on String differs from printing to STDOUT and printing to file

Replies are listed 'Best First'.
Re: String differs from printing to STDOUT and printing to file
by GrandFather (Saint) on Oct 23, 2019 at 10:41 UTC

    The ^@s are most likely null characters in the file. You don't show us how $var gets its value so the nulls could be in the variable. My guess it that the printf to STDOUT isn't showing the nulls in your terminal window even though they are there, but whatever you are using to see the contents of the file is showing the nulls.

    As a test you could pipe the output from your print version of the script to a file then inspect the file in the same way as you did for the script generated file. I expect you will find both files are the same and the problem is in how you generate the contents of $var.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      Yes, piping the output to a file results in the same as printing to the file.
      So it looks like the terminal does that.
      Here is the code for the whole script:
      #!/usr/bin/env perl use strict; use Crypt::Rijndael; use MIME::Base64; my $string = 'QeTEv2804'; sub encrypt { my ($plaintext) = @_; my $password = "uNsY3WSs0hTd"; my $trail = 16 - (length($plaintext) % 16 ); $plaintext .= "\0" x $trail; my $cipher = Crypt::Rijndael->new(pack("A32",$password),Crypt::Rijnd +ael::MODE_CBC()); $cipher->set_iv(pack("A16",$password)); my $crypted = $cipher->encrypt($plaintext); my $encoded = encode_base64($crypted); return $encoded; } sub decrypt { my ($ciphertext) = @_; my $password = "uNsY3WSs0hTd"; my $cipher_nonbase64 = decode_base64($ciphertext); my $cipher = Crypt::Rijndael->new(pack("A32",$password),Crypt::Rijnd +ael::MODE_CBC()); $cipher->set_iv(pack("A16",$password)); my $plaintext = $cipher->decrypt($cipher_nonbase64); return $plaintext; } my $enc = &encrypt($string); my $dec = &decrypt("$enc"); print "Decrypted --> $dec <--\n";
      Pipe the script to file and you should see it.

        Thanks for posting the code, it confirms GrandFather's suggestion; using my advice from here to use Devel::Peek to inspect $dec, it shows:

        SV = PV(0x571d5fd57c40) at 0x560d5ed7d9a0 REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) PV = 0x571d5fe15180 "QeTEv2804\0\0\0\0\0\0\0"\0 CUR = 16 LEN = 18 COW_REFCNT = 0

        As per the Crypt::Rijndael docs, the blocksize for Rijndael is 16 bytes.

        encrypt() seems to NUL pad the string to the required block size:

          $plaintext .= "\0" x $trail;

        That's where your NULs are coming from. If you know that trailing \0s are not ever valid, you can strip them right before your return in decrypt():

        $plaintext =~ s/\0+$//;

        On the other hand, if trailing NUL are a legitimate possibility, you will have to come up with a different plan, such as encoding the real length within your plaintext in encrypt() so you can trim it in the decrypt() sub.

Re: String differs from printing to STDOUT and printing to file
by rjt (Curate) on Oct 23, 2019 at 10:40 UTC

    I can not reproduce your results, in part because your results are strange.

    It would help if you posted a complete example that demonstrates your problem, and enclose that in <code> tags. Here's what I used to test:

    use 5.010; use strict; use warnings; use Carp; my $var = 'QeTEv2804'; printf "%s.\n", $var; open my $debug, '>>', 'debug.txt' or confess "Couldn't open file: $!"; printf $debug "%s.\n", $var; close $debug;

    debug.txt:

    $> hexdump -C debug.txt 00000000 51 65 54 45 76 32 38 30 34 2e 0a |QeTEv2804 +..| 0000000b

    In your output, those control characters are NULs, not line endings, and they somehow come before the `.' in your output, suggesting they are part of $var itself. If I append NULs: $var = "QeTEv2804\0\0\0"; and re-run the above script, the output still looks like this, because those \0s are unprintable:

    QeTEv2804.

    Are you sure this is your actual code? Something seems fishy.

    Edit: Took out irrelevant info from above paragraph and added example that reproduces the OP's result.

    Another thing I'd suggest is to always use the 3-argument open with lexical file handle, as I did in my example. It won't affect your results in this case, but it's a best practice.

Re: String differs from printing to STDOUT and printing to file
by daxim (Curate) on Oct 23, 2019 at 14:03 UTC
    Pipe through cat -t to show invisible characters.
    › printf "QeTEv2804\0\0\0" QeTEv2804 › printf "QeTEv2804\0\0\0" | cat -t QeTEv2804^@^@^@
Re: String differs from printing to STDOUT and printing to file
by haukex (Archbishop) on Oct 23, 2019 at 10:53 UTC

      <shameless plug>Data::Peek combines the two without hexdump or od installed:</shameless plug>

      $ perl -MData::Peek -we'my $pw = "QeTEv2804\c@\c@\c@"; DPeek $pw; DDis +play $pw; DHexDump $pw' PV("QeTEv2804\0\0\0"\0) "QeTEv2804\0\0\0" 0000 51 65 54 45 76 32 38 30 34 00 00 00 QeTEv2804...

      Enjoy, Have FUN! H.Merijn
Re: String differs from printing to STDOUT and printing to file
by hippo (Bishop) on Oct 23, 2019 at 10:24 UTC

    As you are appending, perhaps the file originally terminates with a solo carriage return? Hexdump the file to see for sure. Alternatively, try with an overwrite instead of an append to see if the problem still exists.

      With an overwrite it's the same!
      Thanks, Christian