如何使用Perl的File::Copy复制名称带有特殊字符的文件?

3
我正在尝试将一个位置的所有文件复制到另一个位置,并使用File :: Copy模块和从中提取的copy命令,但现在我面临的问题是,我有一个文件名包含特殊字符的文件,其 ASCII 值为&#253,但在Unix文件系统中,它存储为?。因此,我的问题是,在复制或移动到另一个位置时,copy或move命令是否会考虑这些具有特殊字符的文件, 如果不会,则可能的解决方法是什么?
注意:我不能在Unix中创建带有特殊字符的文件,因为特殊字符会被替换为?,而我在Windows上也无法这样做,因为在Windows上,特殊字符会被编码值替换,例如我的&#253
my $folderpath = 'the_path';
open my $IN, '<', 'path/to/infile';
my $total;
while (<$IN>) {
    chomp;
    my $size = -s "$folderpath/$_";
    print "$_ => $size\n";
    $total += $size;
}
print "Total => $total\n";

感谢:RickF 回答

欢迎提出任何建议。

参考问题Perl文件处理问题


注:此文涉及IT技术相关内容。请您为了更好的理解,尽可能浏览全文,如有需要请自行查阅相关资料。

2
你是从Unix复制到Windows吗?如果是这样,你还需要处理Unix上合法但在Windows上不合法的字符。一个极端的例子是,“\n”是Unix文件名中的一个合法字符。 - dawg
是的。我正在从Unix复制到Windows。 - Rachel
@Ether:我无法获取包含特殊字符的文件大小。 - Rachel
通常情况下,点踩都会附带一个原因,请提供一个原因,以便我改进问题。 - Rachel
你能否使用“?”而不是转义代码或特殊字符来访问文件? - Powertieke
@Powertieke - 我能够使用?代替文件名中的特殊字符打开该文件。 - Rachel
3个回答

3
作为一种解决方法,我建议将所有不支持的字符转换为支持的字符。这可以通过多种方式来实现。例如,您可以使用URI::Escape
use URI::Escape;
my $new_file_name = uri_escape($weird_file_name);

更新:

以下是我如何通过文件的 UTF-8 名称进行复制的方法。我使用的是 Windows 系统。我使用了 Win32::GetANSIPathName 来获取短文件名,然后顺利地完成了复制:

use File::Copy;
use URI::Escape;
use Win32;

use utf8; ## tell perl that source code is in utf-9
use strict;
use warnings;

my $test_file = "IBMýSoftware.txt";
my $from_file = Win32::GetANSIPathName($test_file); ## get "short" name of file
my $to_file   = uri_escape($test_file); ## name with special characters escaped

printf("copy [%s] -> [%s]\n", $from_file, $to_file);
copy($from_file, $to_file);

在Windows上将所有文件复制到新名称后,您可以在Linux上轻松使用它们。
以下是有关utf-8文件打开的一些提示:

@Rachel,我猜脚本无法创建经过转换后的文件名。你能否给出在 uri_escape 函数调用后失败的文件名示例? - Ivan Nevostruev
好的,Win32::GetANSIPathName 从文件名中删除特殊字符。根据手册:“返回FILENAME的ANSI版本。如果长名称无法在系统代码页中表示,则可能是短名称。如果FILENAME在文件系统上不存在,或者文件系统不支持短ANSI文件名,则此函数将使用替换字符将Unicode名称转换为系统代码页。”我猜Windows文件系统(NTFS)支持同一文件的多个名称。这是为了向旧的DOS/Win95程序提供向后兼容性。 - Ivan Nevostruev
@Ivan:我原本以为我们是使用uri_escape来删除特殊字符,现在我对uri_escapeWin32::GetANSIPathName的功能有些困惑,请问你有什么建议吗? - Rachel
但是假设我在Unix系统中使用此脚本,那么它应该可以正常工作,对吧?带有特殊字符的文件存储在Unix文件系统中,我的Perl脚本只是尝试获取具有特殊字符名称的文件的大小,但无法这样做,因此如果我的文件存储在Unix中,则很难将Unix与Win32相关联,对吧? - Rachel
试一下。并尝试使用字符串“IBM?Software.txt”作为名称进行阅读。其中一个应该有效。 - Ivan Nevostruev
显示剩余13条评论

3

第253个字符是ý。我猜在你的Unix系统上没有设置locale(区域设置),或者只有最原始的回退locale生效,这就是为什么你看到一个替换字符的原因。如果我的猜测正确,解决办法就是简单地设置locale,最好是设置为UTF-8 locale,因为它可以处理所有字符,而且Perl甚至不应该成为问题。

> cat 3761218.pl
use utf8;
use strict;
use warnings FATAL => 'all';
use autodie qw(:all);

my $file_name = '63551_106640_63551 IBMýSoftware Delivery&Fulfillment(Div-61) Data IPS 08-20-2010 v3.xlsm';
open my $h, '>', $file_name;

> perl 3761218.pl
> ls 6*
63551_106640_63551 IBMýSoftware Delivery&Fulfillment(Div-61) Data IPS 08-20-2010 v3.xlsm
> LANG=C ls 6* # temporarily cripple locale so that the problem in the question is exhibited
63551_106640_63551 IBM??Software Delivery&Fulfillment(Div-61) Data IPS 08-20-2010 v3.xlsm
> locale | head -1 # show which locale I have set
LANG=de_DE.UTF-8

你能详细说明一下吗,特别是为什么要使用两个Perl文件,以及在Unix中如何设置locale? - Rachel
2
我只使用一个Perl文件。- 我不知道您使用哪个Unix系统,因此无法给出一个好的答案。但是,您可以轻松地自行找到答案。搜索Serverfault档案或使用任何通用的Web搜索引擎,例如Google,以找到所需的文档。 - daxim
我无法理解 LANG=C ls 6*locale | head -1 LANG=de_DE.UTF-8 中发生了什么,你能在代码中提供一些注释来解释这种情况吗?这将有助于我学习并更好地理解它。 - Rachel
好的,我添加了一些注释。在普通命令前加上变量赋值并不是什么特别的事情,这是shell编程基础。阅读更多教程或书籍:http://oreilly.com/catalog/9780596009656/ - daxim
嗯...感谢Daxim的更新,基本上只需将语言环境设置为UTF-8,问题就能得到解决,我的脚本就能够获取包含特殊字符的文件大小,而此前无法实现。 - Rachel

0

以下脚本在我的环境中正常工作:

#!/usr/bin/perl

use strict; use warnings;
use autodie;

use File::Copy qw( copy );
use File::Spec::Functions qw( catfile );

my $fname = chr 0xfd;

open my $out, '>', catfile($ENV{TEMP}, $fname);
close $out;

copy catfile($ENV{TEMP}, $fname) => catfile($ENV{HOME}, $fname);

我无法理解这个脚本,如果您能添加一些注释,我将不胜感激。 - Rachel
@Rachel 这个脚本创建了一个文件,其名称仅由字符代码253组成。然后,将该文件从我的临时目录复制到我的主目录。 - Sinan Ünür
问题中提到的脚本打印所有文件的大小,但如果文件有一些特殊字符,则会忽略它们。令我惊讶的是,#,@,$被脚本接受并给出了大小,但是ý没有被考虑在内,我很难理解为什么会这样,并希望了解您对此的看法,或者如果您可以指导我阅读一些适当的资料,我将不胜感激。 - Rachel
Unur: 我正在尝试理解指令的流程,如果可能的话,如果您可以在代码本身中添加更多的注释,那将非常好,这样我就可以更好地了解它是如何工作的,这会对我很有帮助。 - Rachel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接