Skip to content

Instantly share code, notes, and snippets.

@zr-tex8r
Created March 11, 2026 00:07
Show Gist options
  • Select an option

  • Save zr-tex8r/77b0135da958fa10ff339e7df3096467 to your computer and use it in GitHub Desktop.

Select an option

Save zr-tex8r/77b0135da958fa10ff339e7df3096467 to your computer and use it in GitHub Desktop.
例のアレ🍣
#### 失敗するパターン
use strict;
use File::Temp;
use Cwd;
use Encode;
use Encode::Locale; # console_inはcp932だと仮定する
# STDINからの文字列読込と等価な処理
# ※入力文字列はASCII文字からなるとする
# ※console_inはASCII互換な文字コードとする
my $arch = Encode::decode('console_in', 'windows'); # ASCII列/UTF8フラグON
my $pkg = 'foo'; # ASCII列/UTF-8フラグOFF
my $cwd = Cwd::getcwd(); # CP932バイト列/UTF8フラグOFF
my $temp = File::Temp::tempdir("$cwd/tempXXXX"); # CP932バイト列/UTF8フラグOFF
END { rmdir($temp); }
# これはフラグOFFの文字列だけ連結している
my $path1 = "$temp/$pkg.tar.xz"; # CP932バイト列/UTF8フラグOFF
system("echo", $path1);
# これは想定通り
# $archがフラグONなので全体がフラグONになる
my $path2 = "$temp/$pkg.$arch.tar.xz"; # CP932バイト列/UTF8フラグON
# 意味的にはバイト列だがフラグONなので内部表現がUTF-8のバイト列になっている
# (例えば"あ"(0x82A0)は"C2 82 C2 A0"となっている)
# system()で実際にOSに渡されるのは内部表現のバイト列である:-(
system("echo", "$temp/$pkg.$arch.tar.xz");
# これは文字化けする
#### 姑息な修正方法
use strict;
use File::Temp;
use Cwd;
use Encode;
use Encode::Locale; # console_inはcp932だと仮定する
# STDINからの文字列読込と等価な処理
# ※入力文字列はASCII文字からなるとする
# ※console_inはASCII互換な文字コードとする
my $arch = Encode::decode('console_in', 'windows'); # ASCII列/UTF8フラグON
utf8::downgrade($arch, 1); # フラグOFFの文字列に変換する
my $pkg = 'foo'; # ASCII列/UTF-8フラグOFF
my $cwd = Cwd::getcwd(); # CP932バイト列/UTF8フラグOFF
my $temp = File::Temp::tempdir("$cwd/tempXXXX"); # CP932バイト列/UTF8フラグOFF
END { rmdir($temp); }
# これはフラグOFFの文字列だけ連結している
my $path1 = "$temp/$pkg.tar.xz"; # CP932バイト列/UTF8フラグOFF
system("echo", $path1);
# これは想定通り
# これもフラグOFFの文字列だけ連結している
my $path2 = "$temp/$pkg.$arch.tar.xz"; # CP932バイト列/UTF8フラグOFF
system("echo", "$temp/$pkg.$arch.tar.xz");
# 想定通り
#### マトモな修正方法
# ("保持するデータはバイト列でなく文字列とする"の原則に従う)
use strict;
use File::Temp;
use Cwd;
use Encode;
use Encode::Locale;
# STDINからの文字列読込と等価な処理
# ※入力文字列はASCII文字でなくてもOK
my $arch = Encode::decode('console_in', 'windows'); # Unicode文字列/UTF8フラグON
my $pkg = 'foo'; # ASCII列/UTF-8フラグOFF
my $cwd = Encode::decode('locale_fs', Cwd::getcwd()); # Unicode文字列/UTF8フラグON
my $temp = Encode::decode('locale_fs', File::Temp::tempdir(Encode::encode('locale_fs', "$cwd/tempXXXX"))); # Unicode文字列/UTF-8フラグON
END { rmdir(Encode::encode('locale_fs', $temp)); }
my $path1 = "$temp/$pkg.tar.xz"; # Unicode文字列/UTF8フラグON
# ※Encode::encodeは必ずフラグOFFの値を返す
system("echo", Encode::encode('locale_fs', $path1));
# (もちろん実際はechoじゃなくてパス名を引数にとるコマンドとする)
# これは想定通り
my $path2 = "$temp/$pkg.$arch.tar.xz"; # Unicode文字列/UTF8フラグON
system("echo", Encode::encode('locale_fs', $path2));
# 想定通り
@zr-tex8r
Copy link
Author

zr-tex8r commented Mar 11, 2026

  • system()rmdir()等のOS関連の機能において文字列を実効的に「その内部表現のバイト列」として扱うのが全ての現況 😣
    • どうやらWindows特有の話でもなくUnix系でも同様らしい。
  • 「意味的にバイト列であるがUTF8フラグがON」であること自体は問題ではない。
    • というか、「それが問題になってはいけない」 のが大原則だけど、Perlの標準機能がその大原則を破っている 😲
  • $archは意味的にUnicode文字列であるため、このプログラムでは確かに「文字列とバイト列の連結」をやっていることになるのだが、しかしASCIIしか含まない文字列は「ASCIIのバイト列」とも「CP932のバイト列」とも見なせるので、その前提であれば問題ないはず。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment