“Turn Off” binmode(STDOUT, “:utf8”) Locally

醉酒当歌 提交于 2019-12-06 05:48:32

问题


I Have The following block in 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 other encoding(from a distant subroutine), the data will not display correctly, when receiving cyrillic or other characters. It is the "binmode", that causes the problem.

Can I "turn off" the binmode utf8 locally, for the subroutine only?

I can't remove the global binmode setting and I can't change the distant encoding.


回答1:


One way to achieve this is to "dup" the STD handle, set the duplicated filehandle to use the :raw layer, and assign it to a local version of the STD handle. 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.




回答2:


I like @nwellnhof's approach. Dealing only with Unicode and ASCII - a luxury few enjoy - my instinct would be to leave the bytes as is and selectively make use of Encode to decode()/encode() when needed. If you are able to determine which of your data sources are problematic you could filter/insert decode when dealing with them.

% file koi8r.txt 
koi8r.txt: ISO-8859 text
% cat koi8r.txt 
������ �� ����� � ������� ���. ���
���� ����� ������ ����� �����.
% perl -CO -MEncode="encode,decode" -E 'say decode("koi8-r", <>) ;' koi8r.txt
Американские суда находятся в международных водах. Япония



回答3:


You could use something like Scope::Guard - lexically-scoped resource management to ensure it gets set back to :utf8 when you leave the scope, regardless of 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 awesome for this kind of localizing...):

#!/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}->();
}


来源:https://stackoverflow.com/questions/27801561/turn-off-binmodestdout-utf8-locally

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!