如何在其文件名中创建一个包含Unicode字符的文件,并以可移植的方式打开?



我有一个包含Unicode字符的perl字符串,我想用这个字符串创建一个文件作为文件名。它应该可以在Windows、Linux和Mac上工作,无论使用的语言环境是什么。下面是我的代码:

use strict;
use warnings FATAL => 'all';
use Encode::Locale;
use Encode;
# ファイル.c
my $file = "x{30D5}x{30A1}x{30A4}x{30EB}.c";
$file = encode(locale_fs => $file);
open(my $filehdl, '>', $file) or die("Unable to create file: $!");
close($filehdl);

我使用encode函数是因为,根据这个答案:

Perl将文件名视为不透明的字节串。它们需要按照您的"locale"编码(ANSI代码页)进行编码。

但是,此代码失败,并出现以下错误:

无法创建文件:.perl.pl第15行无效参数。

我更深入地研究了encode如何对字符串进行编码:

my $rep = sprintf '%v02X', $file;
print($rep);

这个打印:

3F.3F.3F.3F.2E.63

在我当前的语言环境(CP-1252)中,它对应于????.c。我们可以看到每个Unicode字符都被一个问号代替了。我认为这里有问号是正常的,因为我的字符串中的字符无法使用CP-1252编码表示。

所以,我的问题是:有没有一种方法来创建一个文件名包含Unicode字符的文件?

对于Windows,有一个模块Win32::LongPath,它不仅允许长文件名,而且允许unicode字符。

我自己写了一个模块,用于我需要的所有类型的文件和dir IO,在Windows上使用这些模块的函数,否则是标准的perl函数,像这样:

use Carp;
use Fcntl qw( :flock :seek );
use constant USE_LONG => ($^O =~ /Win/i) ? 1 : 0;
use if USE_LONG, 'Win32::LongPath', ':funcs';
sub open
{
my $f       = shift; # file
my $m       = shift;    # mode
my $l       = @_ ? (shift) : 'utf8';    # encoding
my $lock    = $m eq '<' ? LOCK_SH : LOCK_EX;
length $l
and $m .= ":$l";
my $h;
USE_LONG ? openL( $h, $m, $f ) : open( $h, $m, $f ) # openL needs REF on Handle!
or confess "Can't open file: '$f' ($^E)";
flock( $h, $lock );
return $h;
}

这样代码是可移植的。它可以在Linux服务器上运行,也可以在我家里的Windows电脑上运行。

最新更新