В Perl, как я могу проверить, действительно ли кодировка, указанная в строке?

Скажем, у меня есть sub, который получает два аргумента: спецификация кодирования и путь к файлу. Затем субпользователь использует эту информацию, чтобы открыть файл для чтения, как показано ниже, разделенный до его сути:

run({
    encoding => 'UTF-16---LE',
    input_filename => 'test_file.txt',
});

sub run {
    my $args = shift;
    my ($enc, $fn) = @{ $args }{qw(encoding input_filename)};

    my $is_ok = open my $in,
        sprintf('<:encoding(%s)', $args->{encoding}),
        $args->{input_filename}
    ;
}

Теперь это кричит:

Cannot find encoding "UTF-16---LE" at E:\Home\...

Что такое right, чтобы гарантировать, что $args->{encoding} содержит допустимую спецификацию кодирования перед интерполяцией во второй аргумент на open?

Update

Информация ниже представлена ​​в надежде, что в какой-то момент она будет полезна кому-то. Я также собираюсь записать отчет об ошибке .

В документах для Encode::Alias вообще не упоминается find_alias. Случайный взгляд на Encode/Alias.pm в моей системе Windows показывает:

# Public, encouraged API is exported by default

our @EXPORT =
  qw (
  define_alias
  find_alias
);

Однако обратите внимание:

#!/usr/bin/env perl

use 5.014;
use Encode::Alias;
say find_alias('UTF-8')->name;

дает:

Use of uninitialized value $find in exists at C:/opt/Perl/lib/Encode/Alias.pm line 25. Use of uninitialized value $find in hash element at C:/opt/Perl/lib/Encode/Alias.pm line 26. Use of uninitialized value $find in pattern match (m//) at C:/opt/Perl/lib/Encode/Alias.pm line 31. Use of uninitialized value $find in lc at C:/opt/Perl/lib/Encode/Alias.pm line 40. Use of uninitialized value $find in pattern match (m//) at C:/opt/Perl/lib/Encode/Alias.pm line 31. Use of uninitialized value $find in lc at C:/opt/Perl/lib/Encode/Alias.pm line 40.

Быть 1) ленив и 2) сначала предположить, что я делаю что-то неправильно, я решил искать чужую мудрость.

В любом случае ошибка связана с тем, что find_alias экспортируется как функция, не проверяя это в коде:

sub find_alias {
    require Encode;
    my $class = shift;
    my $find  = shift;
    unless ( exists $Alias{$find} ) {

Если find_alias не используется как метод, аргумент теперь находится в $class и $find равен undefined.

НТН.

Ответ 1

Вы можете использовать функцию find_encoding в Encode. Хотя, если вы хотите использовать его как слой :encoding, вы также должны проверить perlio_ok. Возможно (но редко) для кодирования, но не поддерживает использование с :encoding:

use Carp qw(croak);
use Encode qw(find_encoding);

sub run {
    my $args = shift;
    my $enc = find_encoding($args->{encoding}) 
      or croak "$args->{encoding} is not a valid encoding";
    $enc->perlio_ok or croak "$args->{encoding} does not support PerlIO";

    my $is_ok = open my $in,
        sprintf('<:encoding(%s)', $enc->name),
        $args->{input_filename}
    ;
}

Примечание: find_encoding обрабатывает псевдонимы, определенные в Encode:: Alias.

Если вам не нужно различать несуществующие кодировки и те, которые не поддерживают :encoding, вы можете просто использовать функцию perlio_ok:

Encode::perlio_ok($args->{encoding}) or croak "$args->{encoding} not supported";

Ответ 2

Encode::Alias->find_alias($encoding_name) возвращает объект, чей атрибут name является именем канонической кодировки при успешном завершении, и false при ошибке.

$ Encode::Alias->find_alias('UTF-16---LE')
$ Encode::Alias->find_alias('UTF-16 LE')
Encode::Unicode  {
    Parents       Encode::Encoding
    Linear @ISA   Encode::Unicode, Encode::Encoding
    public methods (6) : bootstrap, decode, decode_xs, encode, encode_xs, renew
    private methods (0)
    internals: {
        endian   "v",
        Name   "UTF-16LE",
        size   2,
        ucs2   ""
    }
}
$ Encode::Alias->find_alias('Latin9')
Encode::XS  {
    public methods (9) : cat_decode, decode, encode, mime_name, name, needs_lines, perlio_ok, renew, renewed
    private methods (0)
    internals: 140076283926592
}
$ Encode::Alias->find_alias('UTF-16 LE')->name
UTF-16LE
$ Encode::Alias->find_alias('Latin9')->name
iso-8859-15