HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

Perlで日本語を含むフォルダ名の扱いで苦労した話

はじめに

Windows 11のXAMPP上で動くCGIプログラム(Perl)で日本語を含むフォルダ名の扱いに苦労した話。 分かってしまえばなんてことはないんだけど。

環境

> C:\xampp\perl\bin\perl.exe -v

This is perl 5, version 32, subversion 1 (v5.32.1) built for MSWin32-x64-multi-thread

また、フォルダ階層が以下のようになっている。

> ls C:\xampp\日本語を含むフォルダ\


    ディレクトリ: C:\xampp\日本語を含むフォルダ


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2024/07/13      9:28                012345
d-----        2024/07/13      9:28                abcde
d-----        2024/07/13      9:28                あいうえお
d-----        2024/07/13      9:28                かきくけこ

現象

CGIプログラムとしてではなく、単体で動かしたときも同様なので、後者で動かすプログラムとして書いていく。

use utf8;
use File::Find;

my $path = qw(C:\xampp\日本語を含むフォルダ);
if (-e $path) {
    print "OK\n";
} else {
    print "NG\n";
}

sub _process {
    print $File::Find::name . "\n";
    if (-e $File::Find::name) {
        print "OK\n";
    } else {
        print "NG\n";
    }
}
find(\&_process, $path);

これを実行すると、失敗する。

NG
Wide character in warn at C:/xampp/perl/lib/Carp.pm line 293.
Can't stat C:\xampp\譌・譛ャ隱槭r蜷ォ繧繝輔か繝ォ繝: No such file or directory
 at .\test1.pl line 19.

対策1(間違い)

あぁ、内部文字列か、ということで、以下のように書き換えた。

use utf8;
use Encode;
use File::Find;

my $path = qw(C:\xampp\日本語を含むフォルダ);
$path = Encode::encode('Shift_JIS', $path);
if (-e $path) {
    print "OK\n";
} else {
    print "NG\n";
}

sub _process {
    print Encode::encode('Shift_JIS', $File::Find::name) . "\n";
    if (-e Encode::encode('Shift_JIS', $File::Find::name)) {
        print "OK\n";
    } else {
        print "NG\n";
    }
}
find(\&_process, $path);

実行する。

OK
C:\xampp\???{?????????t?H???_
NG
C:\xampp\???{?????????t?H???_/012345
NG
C:\xampp\???{?????????t?H???_/abcde
NG
C:\xampp\???{?????????t?H???_/???¢?????¨
NG
C:\xampp\???{?????????t?H???_/?????????±
NG

・・あれ?

対策2(正解)

いろいろと検証して以下にたどり着く。

use utf8;
use Encode;
use File::Find;

my $path = qw(C:\xampp\日本語を含むフォルダ);
$path = Encode::encode('Shift_JIS', $path);
if (-e $path) {
    print "OK\n";
} else {
    print "NG\n";
}

sub _process {
    print $File::Find::name . "\n";
    if (-e $File::Find::name) {
        print "OK\n";
    } else {
        print "NG\n";
    }
}
find(\&_process, $path);

実行結果。

OK
C:\xampp\日本語を含むフォルダ
OK
C:\xampp\日本語を含むフォルダ/012345
OK
C:\xampp\日本語を含むフォルダ/abcde
OK
C:\xampp\日本語を含むフォルダ/あいうえお
OK
C:\xampp\日本語を含むフォルダ/かきくけこ
OK

そりゃあそうですよね、と。

ソースコードにべた書きされたパスは「UTF-8」から変換された「内部文字列」なので、フォルダの存在確認などをする際には「Shift_JIS」にエンコードしてやる必要があるが、フォルダを読み込んで得られるパス名に関しては「もともとShift_JIS」なので、それを変換するとおかしなことになるのは当たり前。

30分くらいにらめっこしていました。