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

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

I'm looking for a way to encode the standard streams only within one module (file) of a multiple-file package. The "open" pragma works lexically for calls to "open" within a lexical scope, but the "use open ':std', ':encoding(UTF-8)'" declaration has a program-wide effect. For example, the following code doesn't complain:

use warnings; use v5.10; use utf8; my $utf8 = "\x{3bc}\x{1fc6}\x{3bd}\x{3b9}\x{3bd}"; say $utf8; { use open qw(:std :encoding(UTF-8)); say $utf8; }

because a UTF-8 layer is added to STDOUT, STDERR, etc., at compile time and therefore affects the "say $utf8" statement that's outside the lexical scope of the "use open".

Is there any way to restrict the encoding of standard streams to a lexical scope? I don't want to have to say "print $my_fh '...'" every time, and "local *STDOUT", etc., works on dynamic scopes, not lexical scopes.

Replies are listed 'Best First'.
Re: Restrict encoding of std streams to lexical scope
by choroba (Cardinal) on Aug 09, 2013 at 12:33 UTC
    Good question. The only solution I was able to think of uses the "dup" of the handles and a callback. You can add the & prototype to save yourself from typing (sub  ) every time.
    #!/usr/bin/perl use warnings; use strict; use feature 'say'; sub encoding { my $code = shift; open my $STORE_STDOUT, '>&STDOUT' or die $!; binmode STDOUT, 'encoding(UTF-8)'; $code->(); open STDOUT, '>&', $STORE_STDOUT or die $!; } my $utf8 = "\x{3bc}\x{1fc6}\x{3bd}\x{3b9}\x{3bd}"; say 'A:', $utf8; encoding(sub { # Here, no warning is issued. say 'B:', $utf8; }); # End of scope, STDOUT is no longer affected. say 'C:', $utf8;
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Restrict encoding of std streams to lexical scope
by Perlbotics (Archbishop) on Aug 09, 2013 at 12:43 UTC

    I fiddled around with require and open::import(...) but that didn't made things better. Since, you want a lexical-region where STDOUT has UTF-properties, you could emulate that with a prototyped helper function that employed binmode and local to temporarily change STDOUT's behaviour:

    use warnings; use v5.10; use utf8; #-- prototype to setup a block where STDOUT is UTF-8 sub with_utf(&) { open(my $stdout, ">&STDOUT"); #-- STDERR analogue local *STDOUT = $stdout; binmode(*STDOUT, ':encoding(UTF-8)'); shift->(); } my $utf8 = "\x{3bc}\x{1fc6}\x{3bd}\x{3b9}\x{3bd}"; say "Default: $utf8"; # Wide character in say at 1048755.pl line 16 +. with_utf { say "Lexical: $utf8"; # (no warning) }; say "Default: $utf8"; # Wide character in say at 1048755.pl line 22 +.

    Result:

    linux> perl 1048755.pl
    Wide character in say at 1048755.pl line 16.
    Default: μῆνιν
    Lexical: μῆνιν
    Wide character in say at 1048755.pl line 22.
    Default: μῆνιν

    Seems, choroba had the same idea... ;-)

      I even played with require and import in the beginning, as well, to no avail :-)
      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Restrict encoding of std streams to lexical scope
by vsespb (Chaplain) on Aug 13, 2013 at 08:28 UTC
    You can write your own wrapper for print() and use %^H (see perlvar) to control behaviour in lexical scopes.