Как я могу поймать предупреждение "Unicode non-character"? - PullRequest
7 голосов
/ 26 февраля 2011

Как я могу поймать предупреждение "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";

Вывод:

# Unicode non-character 0xffff is illegal for interchange at ./perl1.pl line 11.

Ответы [ 2 ]

14 голосов
/ 26 февраля 2011

A Perl 5.10.0 ⋯ 5.13.8 Ошибка

Я собираюсь предположить, что вы на самом деле не хотите «поймать» это предупреждение, а скорее выжить или проигнорировать его.Если вы действительно хотите поймать это, ну, возможно, есть более простые способы сделать это.

Но первое, что нужно знать, это то, что не существует такой вещи, как недопустимая кодовая точка, только недопустимые кодовые точки длявзаимообмен.

Вам просто нужно использовать no warnings "utf8" для области, где вам нужно использовать полный диапазон Unicode (или больше). Для этого нет необходимости использовать eval. Все, что требуется, - это подавление предупреждений в заданной области.Даже в том, что в новых perls это не нужно.

Так что вместо этого:

$char = chr(0xFFFE);

напишите (на старых perls):

$char = do { no warnings "utf8"; chr(0xFFFE) };

Это тоже ситуацияв случае совпадений с таким символом:

 $did_match = do { no warnings "utf8" ; $char =~ $char);

вызовет предупреждение или смертельный исход, в зависимости от того, сколько лет вашему perl, или вообще ничего, в зависимости от того, насколько новый ваш perl.

Вы можете отключить предупреждения, связанные с utf8, только в тех выпусках, где это имеет значение следующим образом:

no if $^V < 5.13.9, qw<warnings utf8>;

'Исправлено в следующем выпуске'

Действительно интересно то, что они (читай: Perl5Портеры, и в частности, Карл Уильямсон, исправили ошибку, которая требует 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

Самое безопасное, что нужно сделать, это поместить no warnings "utf8" именно в те места, где это нужно.Но нет необходимости в eval!

По состоянию на 5.13.10 и, следовательно, в 5.14, есть три подкатегории предупреждений utf8: surrogate для UTF ‑ 16, nonchar, как описано нижеи non_unicode для supers, также определены ниже.

All-Perl Interchange Безопасен

Вы, вероятно, не хотите подавлять предупреждения «недопустимо для обмена» на выходе, хотяпотому что это правда.Ну, если не использовать кодировку Perl "utf8", которая, как ни странно, не совпадает с кодировкой "UTF‑8".Кодировка "utf8" слабее формального стандарта, потому что позволяет нам делать больше интересных вещей, чем мы могли бы.

Однако , если и только если у вас 100% чистота-perl datapath, вы все равно можете использовать любую кодовую точку, которую вы хотите, включая не-Unicode кодовые точки до ᴍᴀxɪɴᴛ.Это 0x7FFF_FFFF на 32-битных машинах и что-то невероятно большое на 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

Обратите внимание, что на 32-разрядном компьютере последний из них производит следующее:

Integer overflow in hexadecimal number at -e line 1.
got ord 4294967295

Разновидности нехарактерных символов, недопустимых для обмена

ТамНа самом деле это несколько, а на самом деле, несколько разных классов кодовых точек, которые недопустимы для обмена.

  • Любая кодовая точка, такая, что (ord(ᴄᴏᴅᴇᴘᴏɪɴᴛ) & 0xFFFE) == 0xFFFE является истинным.Это охватывает последние две кодовые точки во всех возможных плоскостях.Поскольку он охватывает 17 плоскостей, Unicode определяет 34 таких кодовых пункта.Это не символы, хотя они являются кодовыми точками Unicode.Давайте назовем их Penults .Они подпадают под класс nonchar предупреждения 5.13.10 или выше.

  • 32 кодовых пункта, начинающиеся с U + FDD0.Они гарантированно равны нехарактерам , хотя, конечно, они все еще являются кодовыми точками Unicode.Как и в предыдущем наборе пулт, они тоже подпадают под класс предупреждения nonchar от 5.13.10 или выше.

  • 1024 высоких суррогата и 1024 низких суррогата, которые были выделены какнаклон, чтобы сделать UTF ‑ 16 возможным для всех тех тупых систем, которые пробовали UCS ‑ 2 вместо UTF ‑ 8 или UTF ‑ 32.Это ограничивает диапазон допустимых кодовых точек Unicode, ограничивая их только первыми 21 битными значениями. СУРРОГАТЫ - ТОЧКИ КОДА .Они просто недопустимы для обмена, потому что они не всегда могут быть правильно представлены умным умом UTF ‑ 16.В соответствии с 5.13.10 или выше, они контролируются подклассом предупреждений surrogate.

  • Помимо этого, мы теперь выше диапазона Юникода.Я буду окВсе эти Supers . На 32-битной машине у вас все еще есть (10 или) 11 бит из них по сравнению со стандартными 21 битами, которые дает вам Unicode. Perl может использовать их просто отлично. Это дает всего 2 ** 32 кодовых пункта, которые вы можете использовать в своей программе Perl (ну, или, по крайней мере, 2 ** 31 из-за переполнения со знаком). Вы получаете миллион кодовых точек Unicode, но затем вы получаете пару миллиардов супер кодовых точек сверх тех, которые вы можете использовать в Perl. Если вы работаете с 5.13.10 или выше, вы можете контролировать доступ к ним через подкласс non_unicode предупреждений.

  • Perl по-прежнему следует правилам, касающимся Penults, даже в диапазоне Super. На 32-битном компьютере 480 таких суперпультов , а на 64-битном - гораздо больше.

  • Если вы действительно хотите воспроизвести его непереносимо, то, если у вас есть родные 64-битные целые, у вас есть еще 32 или 33 бита выше того, что дают вам суперсайты. Теперь у вас 18 квинтиллионов 446 квадриллионов 744 триллионов 73 миллиардов 709 миллионов 551 тысяча 616 символов. У вас есть целый эксабайт из различных кодовых точек! Это гораздо больше, чем супер, что я буду называть их Hypermegas . Итак, они не очень переносимы, так как для них требуется действительно 64-битная платформа. Они немного чужие, поэтому, может быть, нам стоит написать это Ὑπέρμεγας , чтобы отпугнуть людей. :) Обратите внимание, что правила против пенсий по-прежнему применяются к гипермагазам.


Программа испытаний

Я написал небольшую программу, которая доказывает, что эти кодовые точки классные.

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

ПРИМЕЧАНИЕ : Последняя строка выше показывает еще одну глупую ошибку в адском коде подсвечивания SO. Заметьте, что последний WɪᴋɪWᴏʀᴅ там, \p{Greek}, вышел из схемы раскрашивания? Это означает, что они ищут только заглавные ASCII идентификаторы. Très passé! Зачем беспокоиться о том, ᴜɴɪᴄᴏᴅᴇ если вы не собираетесь использовать такие вещи, как \p{Uppercase} правильно? Как вы увидите в моей программе, где у меня есть массив @ὑπέρμεγας, мы ᴍᴏᴅᴇʀɴ ᴘʀᴏɢʀᴀᴍᴍɪɴɢ ʟᴀɴɢᴜᴀɢᴇs отлично справляются с этим. ☺

Я, очевидно, не запускал все суперсупер или гиперы. А на 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);
}
3 голосов
/ 26 февраля 2011

Это ошибка времени компиляции, похожая на забвение закрытия регулярного выражения.Если вы отложите компиляцию этого куска во время выполнения, вы можете перехватить его:

#!/usr/bin/env perl
use warnings;

use warnings FATAL => qw(all);

my $character;

eval q{
    $character = "\x{ffff}";
};
if ($@) {
    die "---------- caught error ----------\n";
}

print "something\n";

Вывод:

---------- caught error  ----------

Если вы удалите q после eval, вы 'Вы получите то же поведение, что и ваш скрипт, так как eval {...}; if($@) {...} - это то же самое, что и try {...} catch {...};, но с q это эквивалент строки, который совершенно отличается.

ОБНОВЛЕНИЕ :
Как указывает Том , вам, вероятно, следует просто отключить это предупреждение с помощью no warnings qw(utf8) в узкой области вокруг точки, которую вы устанавливаете или получаете значения такого типа.Вы все еще можете перехватить предупреждения utf8 как ошибки на выходе (или что-либо еще, что отправляет данные за пределы вашей программы):

#!/usr/bin/env perl
use warnings FATAL => qw(all);

my $character;

eval {
    no warnings qw(utf8);
    $character = "\x{ffff}";
};
if ($@) {
    die "---------- caught error  ----------\n";
}

print "something\n";
eval {
    print "something $character else\n";
};
if ($@) {
    die "---------- caught output error  ----------\n";
}

Вывод:

something
---------- caught output error  ----------
...