4

このパス名でこのような奇妙なエンコーディングを取得するために私は何をしましたか?
私のファイル マネージャー (Dolphin) では、パス名は適切に見えます。

#!/usr/local/bin/perl
use warnings;
use 5.014;
use utf8;
use open qw( :encoding(UTF-8) :std );
use File::Find;
use Devel::Peek;
use Encode qw(decode);

my $string;
find( sub { $string = $File::Find::name }, 'Delibes, Léo' );
$string =~ s|Delibes,\ ||;
$string =~ s|\..*\z||;
my ( $s1, $s2 ) = split m|/|, $string, 2;

say Dump $s1;
say Dump $s2;

# SV = PV(0x824b50) at 0x9346d8
#   REFCNT = 1
#   FLAGS = (PADMY,POK,pPOK,UTF8)
#   PV = 0x93da30 "L\303\251o"\0 [UTF8 "L\x{e9}o"]
#   CUR = 4
#   LEN = 16

# SV = PV(0x7a7150) at 0x934c30
#   REFCNT = 1
#   FLAGS = (PADMY,POK,pPOK,UTF8)
#   PV = 0x7781e0 "Lakm\303\203\302\251"\0 [UTF8 "Lakm\x{c3}\x{a9}"]
#   CUR = 8
#   LEN = 16

say $s1;
say $s2;

# Léo
# Lakmé

$s1 = decode( 'utf-8', $s1 );
$s2 = decode( 'utf-8', $s2 );

say $s1;
say $s2;

# L�o
# Lakmé
4

2 に答える 2

13

残念ながら、オペレーティング システムのパス名 API は、別の「バイナリ インターフェイス」であり、予測可能な結果を​​得るためEncode::encodeに使用する必要があります。Encode::decode

ほとんどのオペレーティング システムは、パス名を一連のオクテット (つまり、バイト) として扱います。そのシーケンスを latin-1、UTF-8、またはその他の文字エンコーディングとして解釈するかどうかは、アプリケーションの決定です。したがって、 によって返される値readdir()は単なる一連のオクテットでありFile::Find、パス名を Unicode コード ポイントとして使用する必要があることを認識していません。$File::Find::nameこれは、ディレクトリ パス (指定したもの) と、OS から返された値を を介して連結するだけで形成されます。これによりreaddir()、オクテットでマッシュされたコード ポイントが得られます。

経験則: パス名を OS に渡すときは常に、Encode::encode()それが一連​​のオクテットであることを確認します。OS からパス名を取得するときは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]))
}
于 2011-09-25T16:02:16.247 に答える
-2

エンコーディングが強制されていないため、POSIX ファイルシステム API は壊れています。限目。

多くの問題が発生する可能性があります。たとえば、パス上のさまざまなファイルシステムがエンコードを処理する方法 (および処理する場合) に応じて、パス名に latin1 と UTF-8 の両方を含めることもできます。

于 2011-10-19T16:52:48.983 に答える