如何在现代 Perl 中使用 "use My::defaults" 并默认开启 utf8?

14

我想为自己的“默认使用”创建一个模块,例如:

use My::perldefs;

以下内容是根据 (大多数基于tchrist的帖子。)

use 5.014;
use strict;
use features qw(switch say state);

no warnings;
use warnings qw(FATAL closed threads internal debugging pack substr malloc
                unopened portable prototype inplace io pipe unpack regexp
                deprecated exiting glob digit printf utf8 layer
                reserved parenthesis taint closure semicolon);
no warnings qw(exec newline);

use utf8;
use open qw(:std :utf8);
use charnames qw(:full);
use feature qw(unicode_strings);
use Encode qw(encode decode);
use Unicode::Normalize qw(NFD NFC);
use Carp qw(carp croak confess cluck);
use autodie;

简单来说,想要通过use My::perldefs实现以下目标:

  • 完整且正确的UTF8支持,以及
  • 启用所有现代Perl特性。

根据最近的问题,一个好的起点是uni::perl。它几乎可以满足我所有的需求,只需要添加:

use feature qw(unicode_strings);
use charnames qw(:full);
use Encode qw(encode decode);
use Unicode::Normalize qw(NFD NFC);
use autodie;

我将授予赏金给能够使用有效且正确的方式扩展下面的uni::perl(插入如下)的人。

请帮忙创建一个适用于utf8和现代perl的好的样板。谢谢。


以下是uni::perl的副本。

package My::perldefs;

use 5.014;
BEGIN {
    ${^WARNING_BITS} ^= ${^WARNING_BITS} ^ "\xfc\x3f\xf3\x00\x0f\xf3\xcf\xc0\xf3\xfc\x33\x03";
    $^H |= 0x00000602;
}
m{
use strict;
use warnings;
}x;
use mro ();

BEGIN {
    for my $sub (qw(carp croak confess)) {
        no strict 'refs';
        *$sub = sub {
            my $caller = caller;
            local *__ANON__ = $caller .'::'. $sub;
            require Carp;
            *{ $caller.'::'.$sub } = \&{ 'Carp::'.$sub };
            goto &{ 'Carp::'.$sub };
        };
    }
}

sub import {
    my $me = shift;
    my $caller = caller;
    ${^WARNING_BITS} ^= ${^WARNING_BITS} ^ "\xfc\x3f\xf3\x00\x0f\xf3\xcf\xc0\xf3\xfc\x33\x03";

    $^H |=
          0x00000602 # strict
        | 0x00800000 # utf8
    ;

    # use feature
    $^H{feature_switch} =
    $^H{feature_say}    =
    $^H{feature_state}  = 1;

    # use mro 'c3';
    mro::set_mro($caller, 'c3');

    #use open (:utf8 :std);
    ${^OPEN} = ":utf8\0:utf8";
    binmode(STDIN,   ":utf8");
    binmode(STDOUT,  ":utf8");
    binmode(STDERR,  ":utf8");

    for my $sub (qw(carp croak confess)) {
        no strict 'refs';
        *{ $caller .'::'. $sub } = \&$sub;
    }
    while (@_) {
        my $feature = shift;
        if ($feature =~ s/^://) {
            my $package = $me. '::'. $feature;
            eval "require $package; 1" or croak( "$@" );
            $package->load( $caller );
        }
    }
}

1;

注意:

All of the above is (C): Mons Anderson, C<< <mons at cpan.org> >>

2
相关链接:perl5iperl5Toolkit - daxim
2
另外,ToolSet 也许会引起兴趣。 - bvr
1个回答

9

use feature qw(unicode_strings)很容易,只需要设置$^H{feature_unicode}即可。其他模块也不太难,只需要使用require并显式调用必要的模块函数(例如,EncodeUnicode::Normalize通过Exporter定义了一个接受调用包作为参数的export方法)。棘手的是autodie,它严格按照caller的值进行操作,并通常将其函数注入到My::perldefs包中。我认为这里唯一好的解决方案(除了在My::perldefs中重新实现该模块)是使用goto - 这允许在不更改caller的情况下调用所需的方法,因此方法被注入到正确的命名空间中。最终结果如下:

package My::perldefs;

use 5.014;
BEGIN {
    ${^WARNING_BITS} ^= ${^WARNING_BITS} ^ "\xfc\x3f\xf3\x00\x0f\xf3\xcf\xc0\xf3\xfc\x33\x03";
    $^H |= 0x00000602;
}
m{
use strict;
use warnings;
}x;
use mro ();

BEGIN {
    for my $sub (qw(carp croak confess)) {
        no strict 'refs';
        *$sub = sub {
            my $caller = caller;
            local *__ANON__ = $caller .'::'. $sub;
            require Carp;
            *{ $caller.'::'.$sub } = \&{ 'Carp::'.$sub };
            goto &{ 'Carp::'.$sub };
        };
    }
}

sub import {
    my $me = shift;
    my $caller = caller;
    ${^WARNING_BITS} ^= ${^WARNING_BITS} ^ "\xfc\x3f\xf3\x00\x0f\xf3\xcf\xc0\xf3\xfc\x33\x03";

    $^H |=
          0x00000602 # strict
        | 0x00800000 # utf8
    ;

    # use feature
    $^H{feature_switch} =
    $^H{feature_say}    =
    $^H{feature_state}  =
    $^H{feature_unicode}= 1;

    # use mro 'c3';
    mro::set_mro($caller, 'c3');

    #use open (:utf8 :std);
    ${^OPEN} = ":utf8\0:utf8";
    binmode(STDIN,   ":utf8");
    binmode(STDOUT,  ":utf8");
    binmode(STDERR,  ":utf8");

    #use charnames qw(:full)
    require charnames;
    charnames->import(":full");

    #use Encode qw(encode decode)
    require Encode;
    Encode->export($caller, "encode", "decode");

    #use Unicode::Normalize qw(NFC NFD)
    require Unicode::Normalize;
    Unicode::Normalize->export($caller, "NFC", "NFD");

    for my $sub (qw(carp croak confess)) {
        no strict 'refs';
        *{ $caller .'::'. $sub } = \&$sub;
    }
    while (@_) {
        my $feature = shift;
        if ($feature =~ s/^://) {
            my $package = $me. '::'. $feature;
            eval "require $package; 1" or croak( "$@" );
            $package->load( $caller );
        }
    }

    #use autodie qw(:default)
    #goto needs to be used here to make sure that caller doesn't change
    require autodie;
    @_ = ("autodie", ":default");
    goto &autodie::import;
}

1;

使用autodie部分时,${^OPEN} = ":utf8\0:utf8"; 将停止工作。因此,将在不使用autodie部分的情况下使用它。无论如何,谢谢。换句话说,“使用autodie”与${^OPEN}有些不兼容。 - clt60
刚刚发现了这个链接:https://dev59.com/Gm445IYBdhLWcg3wQoAj#4959646,这是一个已知的 Perl bug。 - clt60

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接