"Disable" binmode (STDOUT, ": utf8") Local

I have the following block at the beginning of my script:

#!/usr/bin/perl5 -w
use strict;
binmode(STDIN, ":utf8");
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

      

In some subroutines, when there is a different encoding (from a remote subroutine), the data will not display correctly when receiving Cyrillic or other characters. It is "binmode" that is causing the problem.

Is it possible to "disable" binmode utf8 locally, just for a subroutine?

I cannot remove the global binmode setting and I cannot change the remote encoding.

+4


source to share


3 answers


One way to achieve this is to "fool" the descriptor STD

, set a duplicate file descriptor to use the layer :raw

, and assign it to the local version of the descriptor STD

. For example the following code

binmode(STDOUT, ':utf8');
print(join(', ', PerlIO::get_layers(STDOUT)), "\n");

{
    open(my $duped, '>&', STDOUT);
    # The ':raw' argument could also be omitted.
    binmode($duped, ':raw');
    local *STDOUT = $duped;
    print(join(', ', PerlIO::get_layers(STDOUT)), "\n");
    close($duped);
}

print(join(', ', PerlIO::get_layers(STDOUT)), "\n");

      

prints



unix, perlio, utf8
unix, perlio
unix, perlio, utf8

      

on my system.

+7


source


I love @nwellnhof's approach. Working with Unicode and ASCII is a luxury few enjoy - my instinct is to leave bytes as they are and selectively use Encode

for decode()/encode()

when needed. If you can determine which of your data sources is problematic, you can filter / insert as decode

you work with them.



% file koi8r.txt 
koi8r.txt: ISO-8859 text
% cat koi8r.txt 
                             .    
                             .
% perl -CO -MEncode="encode,decode" -E 'decode("koi8-r", <>) ;' koi8-r.txt
     . 

      

+3


source


You can use something like Scope :: Guard - lexically scoped resource management to ensure that when it goes out of scope, it reverts to :utf8

, no matter how (return, die, whatever):

#!/usr/bin/perl -w
use strict;

use Scope::Guard qw(guard);

binmode(STDOUT, ':utf8');
print(join(', ', PerlIO::get_layers(STDOUT)), "\n");

{
    # When guard goes out of scope, this sub is guaranteed to be called:
    my $guard = guard {
        binmode(STDOUT, ':utf8');
    };
    binmode(STDOUT, ':raw');
    print(join(', ', PerlIO::get_layers(STDOUT)), "\n");
}

print(join(', ', PerlIO::get_layers(STDOUT)), "\n");

      

Or, if you don't want to include a new dependency like Scope :: Guard (Scope :: Guard is great for this kind of localization ...):

#!/usr/bin/perl -w
use strict;

binmode(STDOUT, ':utf8');
print(join(', ', PerlIO::get_layers(STDOUT)), "\n");

{
    my $guard = PoorMansGuard->new(sub {
        binmode(STDOUT, ':utf8');
    });
    binmode(STDOUT, ':raw');
    print(join(', ', PerlIO::get_layers(STDOUT)), "\n");
}

print(join(', ', PerlIO::get_layers(STDOUT)), "\n");

package PoorMansGuard;

sub new {
    my ($class, $sub) = @_;
    bless { sub => $sub }, $class;
}

sub DESTROY {
    my ($self) = @_;
    $self->{sub}->();
}

      

0


source







All Articles