Domanda

There is following list of files in directory:

  1. 01 Born - Praised - Kissed.flac
  2. 02 Wunschkind.flac
  3. 03 You've got it.flac
  4. 04 Down in this Hole.flac
  5. 05 Wälsungenblut.flac
  6. ... N. 0N Filename

#Yes, these are the songs of Oomph!

and following program on Perl:

use warnings;
use strict;
use utf8;
use open qw( :encoding(UTF-8) :std );

my @dirnames;

while ( (my $dirname = <>) =~ /\S/ ) {
    chomp($dirname);
    push (@dirnames, $dirname);
}

foreach my $dirname (@dirnames) {
    opendir (DIR, $dirname);
    while ( my $file = readdir(DIR) ) {
        if(length($file)>5) {
            print $file , "\n";
            my $newfile;

            $newfile = substr($file, 0, 2);
            $newfile .= '.';
            $newfile .= substr($file, 2);

            rename ($dirname . '\\' . $file, $dirname . '\\' . $newfile) or die $!;
        }
    }
    closedir DIR;
}

that gets the list of directories and renames the files in them by adding dot after number.

Program works correctly on all files, but when it try to rename file with umlaut in the filename, both of the Windows PowerShell and Command Line throw the error that Permission denied at the string with rename function.

How to solve this problem, guys?

UPD. Software:

  • Windows 8 x64
  • ActiveState ActivePerl 1601 (Perl 5.16)
È stato utile?

Soluzione

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();

Altri suggerimenti

You are reading the directory as if it was in UTF-8, but you are really in Windows-1252 encoding. Lose the use open qw(...) and it should work.

Do you have access to "rename.pl"? can you do

perl rename.pl "s/^(\d\d)/$1./" *flac

?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top