如何将具有UTF-8文件名的文件复制到Windows上的Perl中的另一个UTF-8文件名?

时间:2010-02-21 00:29:08

标签: perl utf-8 filenames

例如,给定一个空文件テスト.txt,我如何制作一个名为テスト.txt.copy的副本?

我的第一次破解设法访问该文件并创建新文件名,但副本生成テスト.txt.copy

这是我的第一次破解:

#!/usr/bin/env perl

use strict;
use warnings;

use English '-no_match_vars';
use File::Basename;
use Getopt::Long;

use File::Copy;
use Win32;

my (
    $output_relfilepath,
   ) = process_command_line();

open my $fh, '>', $output_relfilepath or die $!;
binmode $fh, ':utf8';
foreach my $short_basename ( glob( '*.txt') ) {

  # skip the output basename if it's in the glob
  if ( $short_basename eq $output_relfilepath ) {
    next;
  }

  my $long_basename = Win32::GetLongPathName( $short_basename );
  my $new_basename  = $long_basename . '.copy';

  print {$fh} sprintf(
                      "short_basename = (%s)\n" .
                      " long_basename = (%s)\n" .
                      "  new_basename = (%s)\n",
                      $short_basename,
                      $long_basename,
                      $new_basename,
                     );
  copy( $short_basename, $new_basename );
}

printf(
       "\n%s done! (%d seconds elapsed)\n",
       basename( $0 ),
       time() - $BASETIME,
      );

# === subroutines ===

sub process_command_line {

  # default arguments
  my %args
    = (
       output_relfilepath => 'output.txt',
      );

  GetOptions(
             'help'                 => sub { print usage(); exit },
             'output_relfilepath=s' => \$args{output_relfilepath},
            );

  return (
          $args{output_relfilepath},
         );
}

sub usage {
  my $script_name = basename $0;

  my $usage = <<END_USAGE;
======================================================================

Test script to copy files with a UTF-8 filenames to files with
different UTF-8 filenames.  This example tries to make copies of all
.txt files with versions that end in .txt.copy.

  usage: ${script_name} (<options>)

options:

  -output_relfilepath <s>   set the output relative file path to <s>.
                            this file contains the short, long, and
                            new basenames.
                            (default: 'output.txt')

----------------------------------------------------------------------

examples:

  ${script_name}

======================================================================
END_USAGE

  return $usage;
}

以下是执行后output.txt的内容:

short_basename = (BD9A~1.TXT)
 long_basename = (テスト.txt)
  new_basename = (テスト.txt.copy)

我尝试用系统调用替换File::Copy的复制命令:

my $cmd = "copy \"${short_basename}\" \"${new_basename}\"";
print `$cmd`;

并使用Win32 :: CopyFile:

Win32::CopyFile( $short_basename, $new_basename, 'true' );

不幸的是,我在两种情况下得到了相同的结果(テスト.txt.copy)。对于系统调用,打印按预期显示1 file(s) copied.

注意:

5 个答案:

答案 0 :(得分:3)

使用CopyFileW中的Win32API::File功能应该可以实现这一点,这应该包含在草莓中。我自己从来没有弄乱过Unicode文件名,所以我不确定细节。您可能需要使用Encode手动将文件名转换为UTF-16LE(encode('UTF16-LE', $filename))。

答案 1 :(得分:2)

您使用Win32获取长文件名,它会为您提供UTF-8编码的字符串。

但是,您使用普通copy 设置长文件名,它使用C stdlib IO功能。 stdlib函数使用默认的文件系统编码。

在现代的Linux上,通常是UTF-8,但在Windows上(遗憾的是)从来都不是,因为系统默认代码页不能设置为UTF-8。因此,您将在西欧Windows安装中将UTF-8字符串解释为代码页1252字符串,如此处所示。 (在日本的机器上,它被解释为代码页932 - 就像Shift-JIS一样 - 它会像繝�せ繝�那样出现。)

我在Perl中没有这样做,但是我怀疑Win32::CopyFile函数更有可能处理Win32模块中其他位置返回的Unicode路径。< / p>

答案 2 :(得分:1)

使用Encode::Locale

use Encode::Locale;
use Encode;
use File::Copy;

copy( encode(locale_fs => $short_basename),
      encode(locale_fs => $new_basename) ) || die $!;

答案 3 :(得分:0)

我在Windows机器上成功复制了你的问题(Win XP简体中文版),我的结论是问题是由字体引起的。选择Truetype字体而不是Raster字体,看看是否一切正常。

我的实验是:

  1. 我首先将Windows控制台的代码页从默认的936(GBK)更改为65001(UTF-8)。 输入C:&gt; chcp 65001

  2. 我写了一个包含代码的脚本:$ a =“テスト”;打印$ a;并将其保存为UTF-8。

  3. 我从控制台运行脚本,发现“テスト”变成了“テã,¹ƒƒ”,这与您在问题中描述的完全相同。

  4. 我将控制台字体从光栅字体更改为Lucida控制台,控制台屏幕给了我:“テストストトト”,这仍然不太正确,但我认为它越来越接近问题的核心。 / p>

  5. 所以虽然我不是100%肯定,但问题可能是由字体引起的。

    希望这有帮助。

答案 4 :(得分:0)

请参阅https://metacpan.org/pod/Win32::Unicode

#!/usr/bin/perl --
use utf8;
use strict;
use warnings;

my @kebabs = (
  "\x{45B}\x{435}\x{432}\x{430}\x{43F}.txt",               ## ћевап.txt
  "ra\x{17E}nji\x{107}.txt",                               ## ražnjić.txt
  "\x{107}evap.txt",                                       ## ćevap.txt
  "\x{43A}\x{435}\x{431}\x{430}\x{43F}\x{447}\x{435}.txt", ## кебапче.txt
  "kebab.txt",
);

{
    use Win32::Unicode qw/ -native /;
    printW "I \x{2665} Perl"; # unicode console out
    mkpathW 'meat';
    chdirW 'meat';
    for my $kebab ( @kebabs ){
        printW "kebabing the $kebab\n";
        open my($fh), '>:raw', $kebab or dieW Fudge($kebab);
        print $fh $kebab              or dieW Fudge($kebab);
        close $fh                     or dieW Fudge($kebab);
    }
}

sub Fudge {
    use Errno();
    join qq/\n/,
      "Error @_",
      map { "  $_" } int( $! ) . q/ / . $!,
      int( $^E ) . q/ / . $^E,
      grep( { $!{$_} } keys %! ),
      q/ /;
}