Как написать файл, * имя_файла * содержит символы utf8 в Perl?

Я пытаюсь создать файл, содержащий символы не-ascii.

Следующий script работает отлично, если он вызывается с параметром 0 как параметр, но умирает при вызове с 1.

Открывается сообщение об ошибке: Недопустимый аргумент в строке C:\temp\filename.pl 15.

script запускается в пределах 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;

Мне, вероятно, не хватает чего-то, что очевидно для других, но я не могу найти, поэтому я был бы признателен за любой указатель на его решение.

Ответ 1

Прежде всего, говорить "символ UTF-8" является странным. UTF-8 может кодировать любой символ Юникода, поэтому набор символов UTF-8 - это набор символов Юникода. Это означает, что вы хотите создать файл, имя которого содержит символы Юникода, и, более конкретно, символы Unicode, которые не находятся в cp1252.

Я ответил на PerlMonks в прошлом. Ответ скопирован ниже.


Perl обрабатывает имена файлов как непрозрачные строки байтов. Это означает, что имена файлов должны быть закодированы в соответствии с вашим "локальным" кодированием (кодовая страница ANSI).

В Windows обычно используется кодовая страница 1252, поэтому кодировка обычно cp1252. * Однако cp1252 не поддерживает символы тамилов и хинди [или "☺" ].

Windows также предоставляет интерфейс Unicode, известный как "Wide", но Perl не предоставляет доступ к нему с помощью встроенных **. Однако вы можете использовать Win32API::File CreateFileW. IIRC, вам все равно нужно будет кодировать имя файла самостоятельно. Если это так, вы должны использовать UTF-16le в качестве кодировки.

Вышеупомянутый Win32::Unicode, похоже, обрабатывает некоторые грязные работы с использованием Win32API::File для вас. Я также рекомендую начать с этого.

* — Кодовая страница возвращается (как число) системным вызовом GetACP. Подготовьте "cp", чтобы получить кодировку.

** — Поддержка Perl для Windows отстой в некоторых отношениях.

Ответ 2

Следующие действия выполняются в 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);

Ответ 3

нет необходимости кодировать имя файла (по крайней мере, не для 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, Пол