Perl's readdir
uses a legacy interface ("ANSI") since it can only handle file names consisting of bytes due to its unix heritage.
The "ANSI" interface uses a single-byte character encoding known as a code page. Your system's code page is 1251, and it doesn't provide a means of encoding "ä", so file names containing "ä" cannot be returned by readdir
.
You need to avoid this "ANSI" interface (FindFirstFileA
) and gain access to FindFirstFileW
. This will provide the file name in UTF-16le, which you can pass to Win32API::File's MoveFileExW
. Win32::Unicode::Dir's open
+fetch
does just that.
It's a dismal state of affairs. I've been meaning to address it, but it would be an extensive project.
use utf8;
use Win32 qw( );
BEGIN {
binmode(STDOUT, ':encoding(cp'.Win32::GetConsoleOutputCP().')');
binmode(STDERR, ':encoding(cp'.Win32::GetConsoleOutputCP().')');
}
use strict;
use warnings;
use feature qw( say );
use open ':encoding(UTF-8)';
use Encode qw( encode );
use Win32::Unicode::Dir qw( mvtreeW );
use Win32API::File qw( MoveFileExW );
my $dir_qfn = '.';
my $wdir = Win32::Unicode::Dir->new();
$wdir->open($dir_qfn)
or die("Can't open $dir_qfn: ".$wdir->error());
for ($wdir->fetch()) {
next if /^\.\.?\z/;
next if length() <= 5;
say;
my $o_fn = $_;
s/^..\K/./s;
my $n_fn = $_;
MoveFileExW(
encode('UTF-16le', "$dir_qfn/$o_fn\0"),
encode('UTF-16le', "$dir_qfn/$n_fn\0"),
0, # or MOVEFILE_REPLACE_EXISTING
)
or die("Can't rename $o_fn to $n_fn: $^E\n");
}
$wdir->close();