IO:: Дескриптор для получения и отмены юникодных символов
Я думаю, что столкнулся с проблемой с Unicode и IO::Handle. Скорее всего, я делаю что-то не так. Я хочу получить и снять отдельные символы Юникода (не байты) из IO::Handle. Но я получаю удивительную ошибку.
#!/usr/local/bin/perl
use 5.016;
use utf8;
use strict;
use warnings;
binmode(STDIN, ':encoding(utf-8)');
binmode(STDOUT, ':encoding(utf-8)');
binmode(STDERR, ':encoding(utf-8)');
my $string = qq[a Å];
my $fh = IO::File->new();
$fh->open(\$string, '<:encoding(UTF-8)');
say $fh->getc(); # a
say $fh->getc(); # SPACE
say $fh->getc(); # Å LATIN CAPITAL LETTER A WITH RING ABOVE (U+00C5)
$fh->ungetc(ord("Å"));
say $fh->getc(); # should be A RING again.
Сообщение об ошибке из строки ungetc() - "Неверно сформированный символ UTF-8 (неожиданный конец строки), скажем, в строке 21 unicode.pl".\x{00c5}
"не отображается на utf8 в строке 21 unicode.pl" Но это правильный гекс для персонажа, и он должен отображаться на персонажа.
Я использовал шестнадцатеричный редактор, чтобы убедиться, что байты для A-RING верны для UTF-8.
Это кажется проблемой для любого двухбайтового символа.
Последнее слово выводит "\xC5" (буквально четыре символа: обратный слеш, x, C, 5).
И я проверил это, читая из файлов вместо скалярных переменных. Результат тот же.
Это Perl 5, версия 16, Subversion 2 (v5.16.2), созданная для уровня darwin-2.
И скрипт сохраняется в UTF-8. Это было первое, что я проверил.
2 ответа
Я почти уверен, что это доказывает, что происходит серьезная ошибка обработки Unicode, учитывая, что этот вывод:
perl5.16.0 ungettest
ungettest 98896 @ Sun Jan 6 16:01:08 2013: sending normal line to kid
ungettest 98896 @ Sun Jan 6 16:01:08 2013: await()ing kid
ungettest 98897 @ Sun Jan 6 16:01:08 2013: ungetting litte z
ungettest 98897 @ Sun Jan 6 16:01:08 2013: ungetting big sigma
ungettest 98897 @ Sun Jan 6 16:01:08 2013: kid looping on parental input
98897: Unexpected fatalized warning: utf8 "\xA3" does not map to Unicode at ungettest line 40, <STDIN> line 1.
at ungettest line 10, <STDIN> line 1.
main::__ANON__('utf8 "\xA3" does not map to Unicode at ungettest line 40, <ST...') called at ungettest line 40
98896: parent pclose failed: 65280, at ungettest line 28.
Exit 255
производится этой программой:
#!/usr/bin/env perl
use v5.16;
use strict;
use warnings;
use open qw( :utf8 :std );
use Carp;
$SIG{__WARN__} = sub { confess "$$: Unexpected fatalized warning: @_" };
sub ungetchar($) {
my $char = shift();
confess "$$: expected single character pushback, not <$char>" if length($char) != 1;
STDIN->ungetc(ord $char);
}
sub debug {
my $now = localtime(time());
print STDERR "$0 $$ \@ $now: @_\n";
}
if (open(STDOUT, "|-") // confess "$$: cannot fork: $!") {
$| = 1;
debug("sending normal line to kid");
say "From \N{greek:alpha} to \N{greek:omega}.";
debug("await()ing kid");
close(STDOUT) || confess "$$: parent pclose failed: $?, $!";
debug("child finished, parent exiting normally");
exit(0);
}
debug("ungetting litte z");
ungetchar("z") || confess "$$: ASCII ungetchar failed: $!";
debug("ungetting big sigma");
ungetchar("\N{greek:Sigma}") || confess "$$: Unicode ungetchar failed: $!";
debug("kid looping on parental input");
while (<STDIN>) {
chomp;
debug("kid got $_");
}
close(STDIN) || confess "$$: child pclose failed: $?, $!";
debug("parent closed pipe, child exiting normally");
exit 0;
ungetc
добавляет байт к входному потоку. Чтобы вернуть U+00C5, поток должен содержать C3 A5
(кодировка UTF-8 этого символа), а не C5
(ord("Å")
). Использовать IO:: Непрочитанныеunread
вместо.