"Отключить" binmode(STDOUT, ":utf8") Локально

У меня есть следующий блок в начале моего скрипта:

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

В некоторых подпрограммах при наличии другой кодировки (из удаленной подпрограммы) данные не будут отображаться правильно при получении кириллицы или других символов. Это "binmode", который вызывает проблему.

Можно ли "отключить" binmode utf8 локально, только для подпрограммы?

Я не могу удалить глобальную настройку binmode и не могу изменить удаленную кодировку.

2 ответа

Решение

Одним из способов достижения этого является "дублирование" STD установить дубликат дескриптора файла, чтобы использовать :raw слой и назначить его на локальную версию STD справиться. Например, следующий код

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");

печать

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

в моей системе.

Мне нравится подход @nwellnhof. Работая только с Unicode и ASCII - роскошью, которой немногие наслаждаются - мой инстинкт должен был бы оставить байты как есть и выборочно использовать Encode в decode()/encode() при необходимости. Если вы можете определить, какие из ваших источников данных являются проблемными, вы можете отфильтровать / вставить decode когда имеешь дело с ними.

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

Вы можете использовать что-то вроде Scope:: Guard - управление ресурсами с лексической областью видимости. чтобы гарантировать, что он будет возвращен в:utf8 когда вы покидаете область видимости, независимо от того, как (вернуть, умереть, что угодно):

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

Или, если вы не хотите включать новую зависимость, например Scope::Guard (Scope::Guard отлично подходит для такого рода локализации...):

#!/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}->();
}
Другие вопросы по тегам