Почему File::Slurp получает неправильные символы UTF8, когда я использую open ‘:std’, ‘:encoding(UTF-8)’;?

#perl #utf-8

#perl #utf-8

Вопрос:

У меня есть программа Perl 5.30.0 на Ubuntu, где комбинация File::Slurp и open ':std', ':encoding(UTF-8)' приводит к неправильному чтению UTF8:

 use strict;
use warnings;
use open ':std', ':encoding(UTF-8)';
use File::Slurp;

my $text = File::Slurp::slurp('input.txt');
print "$textn";
 

с помощью «input.txt » является текстовым файлом в кодировке UTF8 с таким содержимым (без спецификации).:

 ö
 

Когда я запускаю это, ö отображается как ö . Только когда я удаляю use open... строку, она работает должным образом и ö печатается как ö .

Когда я вручную читаю файл, как показано ниже, все работает так, как ожидалось, и я получаю ö :

 $text = '';
open my $F, '<', "input.txt" or die "Cannot open file: $!";
while (<$F>) {
    $text .= $_;
}
close $F;
print "$textn";
 

Почему это так и как лучше всего поступить здесь? Является open ли прагма устаревшей или я что-то еще упускаю?

Ответ №1:

Как и во многих прагмах, [1] эффект use open лексически ограничен.[2] Это означает, что он влияет только на оставшуюся часть блока или файла, в котором он найден. Такая прагма не влияет на код в функциях за пределами ее области видимости, даже если они вызываются из этой области видимости.

Вам нужно сообщить о желании декодировать поток в File::Slurp. Это невозможно сделать с помощью slurp , но это можно сделать read_file с помощью его binmode параметра.

 use open ':std', ':encoding(UTF-8)';  # Still want for effect on STDOUT.
use File::Slurp qw( read_file );

my $text = read_file('input.txt', { binmode => ':encoding(UTF-8)' });
 

Лучшим модулем является File::Slurper .

 use open ':std', ':encoding(UTF-8)';  # Still want for effect on STDOUT.
use File::Slurper qw( read_text );

my $text = read_text('input.txt');
 

File::Slurper read_text по умолчанию использует декодирование с использованием UTF-8.


Без модулей вы могли бы использовать

 use open ':std', ':encoding(UTF-8)';

my $text = do {
   my $qfn = "input.txt";
   open(my $F, '<', $qfn)
      or die("Can't open file "$file": $!n");
   local $/;
   <$fh>
};
 

Конечно, это не так ясно, как предыдущие решения.


  1. Другие известные примеры включают use VERSION , use strict , use warnings , use feature и use utf8 .
  2. Влияние на STDIN, STDOUT и STDERR из :std является глобальным.

Комментарии:

1. Предупреждение об опечатке: похоже, вы хотели вставить <$F> последнюю строку блока do в вашем примере немодульного подхода.

Ответ №2:

На самом деле это не ответ на ваш вопрос, но мой любимый модуль ввода-вывода файлов в наши дни — Path::Tiny .

 use Path::Tiny;
my $text = path('input.txt')->slurp_utf8;