我怎么能抓住"Unicode非角色"警告?

sid*_*com 7 unicode perl warnings character try-catch

我怎么能抓住"Unicode非字符0xffff是非法的交换" - 警告?

#!/usr/bin/env perl
use warnings;
use 5.012;
use Try::Tiny;

use warnings FATAL => qw(all);

my $character;

try {
    $character = "\x{ffff}";
} catch {
    die "---------- caught error ----------\n";
};

say "something";
Run Code Online (Sandbox Code Playgroud)

输出:

# Unicode non-character 0xffff is illegal for interchange at ./perl1.pl line 11.
Run Code Online (Sandbox Code Playgroud)

tch*_*ist 14

一个Perl 5.10.0⋯5.13.8 Bug

我会假设你实际上并不想"抓住"这个警告,而是为了生存或忽略它.如果你真的想抓住它,那么可能有更简单的方法来做到这一点.

但首先要知道的是,没有非法代码点,只有代码点对交换无效.

您只需no warnings "utf8"在需要使用完整Unicode范围(或更多)的范围内使用a . 没有必要使用eval这个.所需要的只是一个范围警告抑制.即使在新的perls上也没有必要.

所以不是这样的:

$char = chr(0xFFFE);
Run Code Online (Sandbox Code Playgroud)

写(在旧的perls上):

$char = do { no warnings "utf8"; chr(0xFFFE) };
Run Code Online (Sandbox Code Playgroud)

这也是涉及这样一个角色的模式匹配的情况:

 $did_match = do { no warnings "utf8" ; $char =~ $char);
Run Code Online (Sandbox Code Playgroud)

根据你的perl的年龄大小,取决于你的perl的新程度,会引起警告或致命.

您只能在以这种方式重要的版本上禁用与utf8相关的警告:

no if $^V < 5.13.9, qw<warnings utf8>;
Run Code Online (Sandbox Code Playgroud)

'固定在下一个版本'

真正有趣的是,他们(阅读:Perl5 Porters,特别是Karl Williamson)已经修复了需要一个no warnings "utf8"警卫才能使用任何代码点的错误.它只是您可能必须小心的输出.看:

% perl5.10.0 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Unicode character 0xfffe is illegal at -e line 1.

% perl5.11.3 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Unicode non-character 0xfffe is illegal for interchange at -e line 1.

% perl5.12.0 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Unicode non-character 0xfffe is illegal for interchange at -e line 1.

% perl5.12.3 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Unicode non-character 0xfffe is illegal for interchange at -e line 1.

% perl5.13.0 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Unicode non-character 0xfffe is illegal for interchange at -e line 1.

% perl5.13.8 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Unicode non-character 0xfffe is illegal for interchange at -e line 1.

% perl5.13.9 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Ok

% perl5.13.10 -Mwarnings=FATAL,all -E 'my $char = chr(0xFFFE); say "Ok"'
Ok
Run Code Online (Sandbox Code Playgroud)

最安全的事情就是放在no warnings "utf8"你需要的地方.但是没有必要eval!

从5.13.10开始,因此在5.14中,有utf8警告的三个子类别:surrogate对于UTF-16,nonchar如下所述,non_unicode对于supers,也在下面定义.

全Perl交换是安全的

但是,您可能不希望在输出上抑制"非法交换"警告,因为这是真的.好吧,除非你使用Perl的"utf8"编码,这与"UTF?8"编码不同,奇怪的是.该"utf8"编码laxer比正式的标准,因为它允许我们做更多有趣的事情,比我们所能,否则.

但是,当且仅当您拥有100%纯perl数据路径时,您仍然可以使用任何所需的代码点,包括最多为ᴍᴀxɪɴᴛ的非unicode代码点.那是32位机器上的0x7FFF_FFFF,在64位机器上有一些难以言表的巨大:0xFFFF_FFFF_FFFF_FFFF!那不仅仅是一个超级; 这是一个超级巨星!

% perl -Mwarnings -CS -E 'my $a = chr(0xFFFF_FFFF); say $a ' | 
  perl -Mwarnings -CS -nlE 'say "got ord ", ord'
Code point 0xFFFFFFFF is not Unicode, may not be portable at -e line 1.
got ord 4294967295

% perl -Mwarnings -CS -E 'no warnings "utf8"; my $a = chr(0xFFFF_FFFF); say $a' |
 perl -Mwarnings -CS -nlE 'say "got ord ", ord'
got ord 4294967295

% perl -Mwarnings -CS -E 'no warnings "utf8"; my $a = chr(0xFFFF_FFFF_FFFF_FFFF); say $a' |
  perl -Mwarnings -CS -nlE 'say "got ord ", ord'
Hexadecimal number > 0xffffffff non-portable at -e line 1.
got ord 18446744073709551615

% perl -Mwarnings -CS -E 'no warnings qw[ utf8 portable ]; my $a = chr(0xFFFF_FFFF_FFFF_FFFF);  say $a ' |
  perl -Mwarnings -CS -nlE 'say "got ord ", ord'
got ord 18446744073709551615
Run Code Online (Sandbox Code Playgroud)

请注意,在32位计算机上,最后一台计算机会产生以下结果:

Integer overflow in hexadecimal number at -e line 1.
got ord 4294967295
Run Code Online (Sandbox Code Playgroud)

交换非法的非品种品种

有几个 - 实际上是相当多 - 不同类别的代码点,这些代码点对于交换是不合法的.

  • 任何代码点 (ord(?????????) & 0xFFFE) == 0xFFFE都是如此.这涵盖了所有可能平面中的最后两个代码点.因为它跨越17个平面,因此Unicode定义了34个这样的代码点.虽然它们是Unicode代码点,但它们不是字符.我们称之为Penults.他们属于nonchar5.13.10或更高级别的警告级别.

  • 32个代码点从U + FDD0开始.这些保证是非字符的,当然它们仍然是Unicode代码点.与之前的阴谋集一样,这些也属于nonchar5.13.10或更高级别的警告级别.

  • 1024个高代理和1024个低代理,这些代理被划分为slop,使得所有那些尝试使用UCS-2而不是UTF-8或UTF-32的哑系统都可以使用UTF-16.这削弱了有效Unicode代码点的范围,将它们限制为仅前21位.代理商仍然是代码点.它们只对交换无效,因为它们不能总是被脑死亡聪明的UTF-16正确表示.在5.13.10或更高版本下,这些由surrogate警告子类控制.

  • 除此之外,我们现在高于Unicode范围.我会称这些超级明星.在32位计算机上,您仍然有(10或11位)超出Unicode为您提供的标准21位.Perl可以使用这些就好了.这样可以在Perl程序中使用2**32个总代码点(好吧,至少2**31,因为有符号溢出).您获得了一百万个Unicode代码点,但是除了可以在Perl中使用的代码点之外,您还可以获得几十亿个超级代码点.如果您运行的是5.13.10或更高版本,则可以通过non_unicode警告子类控制对这些内容的访问.

  • Perl仍然遵循有关Penults的规则甚至超级范围.在32位机器上有480个这样的Superpenults,而在64位机器上则更多.

  • 如果你真的想以非便携式方式播放,那么如果你有64位的本地内存,那么你可以拥有超过超级优惠的32或33位.你现在有18个quintillion 446千万亿744万亿73亿7千7百万551千和616个字符.你有一个整体艾字节独特码点!这远远超出了超级我将称之为Hypermegas.好的,因此它们不是非常便携,因为它们需要真正的64位平台.他们有点陌生,所以也许我们应该写那些scπέρμεγας 来吓跑人们.:)请注意,针对阴茎的规则仍然适用于hypermegas.


测试计划

我写了一个小程序,证明这些代码点很酷.

testing Penults             passed all 34 codepoints
testing Super_penults       passed all 480 codepoints
testing Noncharacters       passed all 32 codepoints
testing Low_surrogates      passed all 1024 codepoints
testing High_surrogates     passed all 1024 codepoints
testing Supers              passed all 8 codepoints
testing ?????????            passed all 10 codepoints
Run Code Online (Sandbox Code Playgroud)

注意:上面的最后一行显示了SO的地狱突出显示代码中的又一个愚蠢错误.请注意最后一个WɪᴋɪWᴏʀᴅ那个,那个\p{Greek},被排除在着色方案之外?这意味着他们只寻找大写的ASCII标识符. Trèspassé! 如果你不打算使用\p{Uppercase}正确的东西,为什么还要接受ᴜɴɪᴄᴏᴅᴇ ?正如你在我的程序中看到的那样,我有一个@?????????数组,我们ᴍᴏᴅᴇʀɴᴘʀᴏɢʀᴀᴍᴍɪɴɢhandles处理完全没问题.☺

我显然没有运行所有的超级或超级.在32位计算机上,您只能获得4个经过测试的超级计算机.我也没有测试任何超级成品.

这是测试程序,它在5.10及更高版本的所有版本上运行得很干净.

#!/usr/bin/env perl
#
# hypertest - show how to safely use code points not legal for interchange in Perl
# 
# Tom Christiansen
# tchrist@perl.com
# Sat Feb 26 16:38:44 MST 2011

use utf8;
use 5.10.0;
use strict;
use if $] > 5.010, "autodie";
use warnings FATAL => "all";

use Carp;

binmode(STDOUT, ":utf8");
END { close STDOUT }

$\ = "\n";

sub ghex(_);

my @penults = map { 
    (0x01_0000 * $_) + 0xfffE, 
    (0x01_0000 * $_) + 0xfffF, 
} 0x00 .. 0x10;

my @super_penults = map { 
    (0x01_0000 * $_) + 0xfffE, 
    (0x01_0000 * $_) + 0xfffF, 
} 0x10 .. 0xFF;

my @low_surrogates  = map { 0xDC00 + $_ } 0x000 .. 0x3FF;
my @high_surrogates = map { 0xD800 + $_ } 0x000 .. 0x3FF;

my @noncharacters = map { 0xFDD0 + $_ } 0x00 .. 0x1F;

my @supers = ( 
    0x0011_0000,  0x0100_0000,  0x1000_0000,  0x1F00_0000,  
    0x1FFF_FFFF,  0x3FFF_FFFF,  0x7FFF_FFFF,  0x7FFF_FFFF,  
);

# these should always work anywhere 
my @????????? = ( 
    0x8000_0000,   0xF000_0000,   
    0x3FFF_FFFF,   0xFFFF_FFFF,  
);

####
# now we go fishing for 64-bit ?????????
####

eval q{
    use warnings FATAL => "overflow";
    no  warnings "portable";
    push @????????? => ( 
        0x01_0000_0000, 
        0x01_FFFF_FF00,
    );
};
eval q{
    use warnings FATAL => "overflow";
    no  warnings "portable";
    push @????????? => (
        0x0001_0000_0000_0000,
        0x001F_0000_0000_0000,
        0x7FFF_FFFF_FFFF_FFFF,
        0xFFFF_FFFF_FFFF_FFFF,
    );
};

# more than 64??
eval q{
    use warnings FATAL => "overflow";
    no  warnings "portable";
    push @????????? => (
        0x01_0001_0000_0000_0000,
        0x01_7FFF_FFFF_FFFF_FFFF,
        0x01_FFFF_FFFF_FFFF_FFFF,
    );
    1;
};


my @testpairs = (
    penults         => \@penults,
    super_penults   => \@super_penults,
    noncharacters   => \@noncharacters ,
    low_surrogates  => \@low_surrogates,
    high_surrogates => \@high_surrogates,
    supers          => \@supers,
    ?????????       => \@?????????,   
);

while (my($name, $aref) = splice(@testpairs, 0, 2)) {
    printf "testing %-20s", ucfirst $name;

    my(@passed, @failed);

    for my $codepoint (@$aref) {

        use warnings FATAL => "all";

        my $char = do {
            # next line not needed under 5.13.9 or better: HURRAY!
            no warnings "utf8";
            chr(0xFFFF) && chr($codepoint);
        };

        my $regex_ok = do {
            # next line not needed under 5.13.9 or better: HURRAY!
            no warnings "utf8";
            $char =~ $char;
            1;
        };

        my $status = defined($char) && $regex_ok;

        push @{ $status ? \@passed : \@failed }, $codepoint;
    }

    my $total  = @$aref;
    my $passed = @passed;
    my $failed = @failed;

    given($total) {
        when ($passed)  { print "passed all $total codepoints" }
        when ($failed)  { print "failed all $total codepoints" }
        default         {
            print "of $total codepoints, failed $failed and passed $passed";
            my $flist = join(", ", map { ghex } @failed);
            my $plist = join(", ", map { ghex } @passed);
            print "\tpassed: $plist";
            print "\tfailed: $flist";
        }
    }

}

sub ghex(_) {
    my $num = shift();
    my $hex = sprintf("%X", $num);
    return $hex if length($hex) < 5;
    my $flip = reverse $hex;
    $flip =~ s<
        ( \p{ahex} \p{ahex} \p{ahex} \p{ahex} )
        (?= \p{ahex} )
        (?! \p{ahex}* \. )
    ><${1}_>gx;
    return "0x" . reverse($flip);
}
Run Code Online (Sandbox Code Playgroud)