Perl IO::File и использовать open qw(:utf8)
IO::File->open(), похоже, не учитывает использование open () в следующей программе, что странно для меня и, кажется, противоречит документации. Или, может быть, я делаю это неправильно. Переписать мой код, чтобы он не использовал IO:: File, не должно быть сложно.
Я ожидаю, что результат будет
$VAR1 = \"Hello \x{213} (r-caret)";
Hello ȓ (r-caret)
Hello ȓ (r-caret)
Hello ȓ (r-caret)
Но я получаю эту ошибку: "Упс: искаженный символ UTF-8 (неожиданный конец строки) при печати в строке./run.pl 33".
Мне это совсем не кажется правильным.
#!/usr/local/bin/perl
use utf8;
use v5.16;
use strict;
use warnings;
use warnings qw(FATAL utf8);
use diagnostics;
use open qw(:std :utf8);
use charnames qw(:full :short);
use File::Basename;
my $application = basename $0;
use Data::Dumper;
$Data::Dumper::Indent = 1;
use Try::Tiny;
my $str = "Hello ȓ (r-caret)";
say Dumper(\$str);
open(my $fh, '<', \$str);
print while ($_ = $fh->getc());
close($fh);
print "\n";
try {
use IO::File;
my $fh = IO::File->new();
$fh->open(\$str, '<');
print while ($_ = $fh->getc());
$fh->close();
print "\n";
}
catch {
say "\nOops: $_";
};
try {
use IO::File;
my $fh = IO::File->new();
$fh->open(\$str, '<:encoding(UTF-8)');
print while ($_ = $fh->getc());
$fh->close();
print "\n";
}
catch {
say "\nOops: $_";
};
2 ответа
Я верю, что здесь происходит use open
это лексическая прагма, означающая, что это влияет только на призывы к open()
в той же лексической сфере. Лексическая область действия - это когда код находится в одном блоке. IO::File->open
это обертка вокруг open()
и так зовет open()
вне его лексической сферы.
{
use open;
...same lexical scope...
{
...inner lexical scope...
...inherits from the outer...
}
...still the same lexical scope...
foo();
}
sub foo {
...outside "use open"'s lexical scope...
}
В приведенном выше примере, хотя foo()
называется внутри use open
Лексическая сфера, код внутри foo()
находится снаружи и, следовательно, не под его влиянием.
Было бы вежливо, если бы IO::File унаследовал open.pm. Это не тривиально, но возможно. Похожая проблема мучает автодие. Это было исправлено, и исправление могло работать в IO::File.
[Это не ответ, а уведомление об ошибке, которая не помещается в комментарии.]
Файлы могут содержать только байты. $str
содержит значения, которые не являются байтами Следовательно,
open(my $fh, '<', \$str)
не имеет смысла. Так должно быть
open(my $fh, '<', \encode_utf8($str))
use utf8;
use v5.16;
use strict;
use warnings;
use warnings qw(FATAL utf8);
use open qw( :std :utf8 );
use Encode qw( encode_utf8 );
use Data::Dumper qw( Dumper );
sub dump_str {
local $Data::Dumper::Useqq = 1;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;
return Dumper($_[0]);
}
for my $encode (0..1) {
for my $orig ("\x{213}", "\x{C9}", substr("\x{C9}\x{213}", 0, 1)) {
my $file_ref = $encode ? \encode_utf8($orig) : \$orig;
my $got = eval { open(my $fh, '<', $file_ref); <$fh> };
printf("%-10s %-6s %-9s => %-10s => %s\n",
$encode ? "bytes" : "codepoints",
defined($got) && $orig eq $got ? "ok" : "not ok",
dump_str($orig),
dump_str($$file_ref),
defined($got) ? dump_str($got) : 'DIED',
);
}
}
Выход:
codepoints ok "\x{213}" => "\x{213}" => "\x{213}"
codepoints not ok "\311" => "\311" => DIED
codepoints not ok "\x{c9}" => "\x{c9}" => DIED
bytes ok "\x{213}" => "\310\223" => "\x{213}"
bytes ok "\311" => "\303\211" => "\x{c9}"
bytes ok "\x{c9}" => "\303\211" => "\x{c9}"