【问题标题】:How do I write a file whose *filename* contains utf8 characters in Perl?如何在 Perl 中编写 *filename* 包含 utf8 字符的文件?
【发布时间】:2011-11-04 13:51:42
【问题描述】:

我正在努力创建一个包含非 ascii 字符的文件。

如果使用0 作为参数调用以下脚本,但使用1 调用时会死机,则以下脚本可以正常工作。

错误消息是 open: Invalid argument at C:\temp\filename.pl 第 15 行。

脚本在cmd.exe内启动。

我希望它编写一个文件名(取决于参数)äöü.txtäöü☺.txt。但我无法创建包含笑脸的文件名。

use warnings;
use strict;

use Encode 'encode';

#   Text is stored in utf8 within *this* file.
use utf8;

my $with_smiley = $ARGV[0];

my $filename = 'äöü' . 
  ($with_smiley ? '☺' : '' ).
   '.txt';

open (my $fh, '>', encode('cp1252', $filename)) or die "open: $!";

print $fh "Filename: $filename\n";

close $fh;

我可能遗漏了一些对其他人来说很明显的东西,但我找不到,所以如果有任何解决这个问题的建议,我将不胜感激。

【问题讨论】:

  • 如果让 open 死掉会得到什么错误信息?
  • @mugen:我已经更改了脚本让它死掉并添加了错误消息。
  • cp1252 是怎么回事?该代码页是否包含此字符?您绝对不是在这里创建 UTF8 文件名!
  • 为什么需要一个包含 Unicode 字符的文件名?也许你可以解决这个问题。

标签: windows perl utf-8 filenames cmd


【解决方案1】:

不需要对文件名进行编码(至少在 linux 上不需要)。此代码适用于我的 linux 系统:

use warnings;
use strict;

#   Text is stored in utf8 within *this* file.
use utf8;

my $with_smiley = $ARGV[0] || 0;

my $filename = 'äöü' .
  ($with_smiley ? '?' : '' ).
     '.txt';

open my $fh, '>', $filename or die "open: $!";

binmode $fh, ':utf8';

print $fh "Filename: $filename\n";

close $fh;

HTH,保罗

【讨论】:

  • 如果我不 encode() 文件名(并传递参数 0),文件名将类似于 äöü.txt
  • 笑脸字符实际上是v63,而不是?
  • 那是 utf8 好的;如果你没有使用支持 utf8 的系统,它会显示名称错误,但名称是正确的。
  • @trip 我在 pavel 的代码中看到 ?,在 René 的代码中看到 (笑脸)。
  • 我猜三人组提到了 äöü.txt.. 呵呵,这样的问题必须以字符混乱结束:P
【解决方案2】:

首先,说“UTF-8 字符”很奇怪。 UTF-8可以编码任何Unicode字符,所以UTF-8字符集就是Unicode字符集。这意味着您要创建名称包含 Unicode 字符的文件,更具体地说,是 cp1252 中不包含的 Unicode 字符。

我过去曾在 PerlMonks 上 answered 这个。答案复制如下。


Perl 将文件名视为不透明的字节字符串。这意味着文件名需要根据您的“语言环境”的编码(ANSI 代码页)进行编码。

在 Windows 中,代码页 1252 是常用的,因此编码通常为 cp1252。* 但是,cp1252 不支持泰米尔语和印地语字符[或“☺”]。

Windows 还提供“Unicode”又名“Wide”接口,但 Perl 不提供使用内置函数**访问它。不过,您可以使用Win32API::FileCreateFileW。 IIRC,您仍然需要自己编码文件名。如果是这样,您将使用UTF-16le 作为编码。

上述Win32::Unicode 似乎为您处理了使用Win32API::File 的一些脏活。我也建议从那个开始。

* — 代码页由GetACP 系统调用返回(作为数字)。在前面加上“cp”来获取编码。

** — Perl 对 Windows 的支持在某些方面很糟糕。

【讨论】:

  • 这完全等同于“Windows 对 POSIX 文件名的支持在某些方面很糟糕。”
  • @ikegami,Widows 确实声称在某些领域符合 POSIX,特别是文件系统和文件命名。 technet.microsoft.com/en-us/library/cc976809.aspx
  • @tchrist,即使这是真的(Ven'Tatsu 似乎另有说法),这与手头的话题无关。 Perl 声称支持 Windows,但它不在这方面。
  • @ikegami,我认为现实存在于 Perl 不支持 Widows 和 Windows 不支持 POSIX 之间。 Windows 上的某些 POSIX 功能仅在可执行文件已为 POSIX 子系统标志集编译时可用。不幸的是,这个标志还改变了其他违反 Windows 开发人员最不意外原则的行为。
  • @Ven'Tatsu,如果您尝试在使用 iso-8859-1 语言环境的 POSIX 系统上创建名为“☺”的文件,您将遇到同样的问题。这与 POSIX 完全没有关系,所有都与使用过时接口的 Perl 无关,而不是使用 CreateFileW。 tchrist 正在毫无根据地抨击 MS。
【解决方案3】:

以下运行在 Windows 7、ActiveState Perl 上。它将“hello there”写入名称中带有希伯来字符的文件:

#-----------------------------------------------------------------------
# Unicode file names on Windows using Perl
# Philip R Brenan at gmail dot com, Appa Apps Ltd, 2013
#-----------------------------------------------------------------------

use feature ":5.16";
use Data::Dump qw(dump);
use Encode qw/encode decode/;
use Win32API::File qw(:ALL);

# Create a file with a unicode name

my $e  = "\x{05E7}\x{05EA}\x{05E7}\x{05D5}\x{05D5}\x{05D4}".
         "\x{002E}\x{0064}\x{0061}\x{0074}\x{0061}"; # File name in UTF-8
my $f  = encode("UTF-16LE", $e);  # Format supported by NTFS
my $g  = eval dump($f);           # Remove UTF ness
   $g .= chr(0).chr(0);           # 0 terminate string
my $F  = Win32API::File::CreateFileW
 ($g, GENERIC_WRITE, 0, [], OPEN_ALWAYS, 0, 0); #  Create file via Win32API
say $^E if $^E;                   # Write any error message

# Write to the file

OsFHandleOpen(FILE, $F, "w") or die "Cannot open file";
binmode FILE;                      
print FILE "hello there\n";      
close(FILE);

【讨论】:

  • 我认为您使用my $g = eval dump($f); 来降级标量?如果是这样,使用utf8::downgrade( my $g = $f ); 会好得多,但没有理由降级,因为encode 总是产生降级的标量。
猜你喜欢
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
  • 2013-10-15
  • 2016-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多