К сожалению, API путей к вашей операционной системе - это еще один «двоичный интерфейс», в котором вам придется использовать Encode::encode
и Encode::decode
для получения предсказуемых результатов.
Большинство операционных систем обрабатывают имена путей как последовательность октетов (т.е. байты).Решение о том, следует ли интерпретировать эту последовательность как латинское-1, UTF-8 или другое кодирование символов.Следовательно, значение, возвращаемое readdir()
, является просто последовательностью октетов, и File::Find
не знает, что вы хотите указать путь в качестве кодовых точек Unicode.Он формирует $File::Find::name
, просто конкатенируя путь к каталогу (который вы указали) со значением, возвращаемым вашей ОС через readdir()
, и таким образом вы получаете кодовые точки, смешанные с октетами.
Правило большого пальца: всякий раз, когдапередавая имена путей в ОС, Encode::encode()
это, чтобы убедиться, что это последовательность октетов.При получении имени пути из ОС, Encode::decode()
оно соответствует набору символов, в котором оно требуется вашему приложению.
Вы можете заставить свою программу работать, вызывая find
следующим образом:
find( sub { ... }, Encode::encode('utf8', 'Delibes, Léo') );
А затем вызывать Encode::decode()
при использовании значения $File::Find::name
:
my $path = Encode::decode('utf8', $File::Find::name);
Чтобы быть более понятным, вот как $File::Find::name
было сформировано:
use Encode;
# This is a way to get $dir to be represented as a UTF-8 string
my $dir = 'L' .chr(233).'o'.chr(256);
chop $dir;
say "dir: ", d($dir); # length = 3
# This is what readdir() is returning:
my $leaf = encode('utf8', 'Lakem' . chr(233));
say "leaf: ", d($leaf); # length = 7
$File::Find::name = $dir . '/' . $leaf;
say "File::Find::name: ", d($File::Find::name);
sub d {
join(' ', map { sprintf("%02X", ord($_)) } split('', $_[0]))
}