4

我的脚本开头有以下块:

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

在某些子程序中,当有其他编码时(来自远处的子程序),当接收到西里尔字符或其他字符时,数据将无法正确显示。导致问题的是“binmode”。

我可以在本地“关闭”binmode utf8,仅用于子程序吗?

我无法删除全局 binmode 设置,也无法更改远程编码。

4

3 回答 3

7

实现此目的的一种方法是“复制”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

在我的系统上。

于 2015-01-06T15:49:03.813 回答
3

我喜欢@nwellnhof 的方法。只处理 Unicode 和 ASCII - 很少有人喜欢的奢侈品 - 我的直觉是保留字节原样,并在需要时有选择地使用Encodeto decode()/encode()。如果您能够确定哪些数据源有问题,则可以decode在处理它们时过滤/插入。

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

您可以使用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}->();
}
于 2019-08-08T09:25:23.527 回答