Как вы меняете кодировку файла, используя Perl?

Я пишу Perl-скрипт, который создает XML-файл "settings.xml". (Использование XML::Writer). Я хотел бы, чтобы файл был закодирован в UCS-2 с прямым порядком байтов, но я не уверен, как.

Я пробовал такие вещи, как: open(my $output, "> :encoding(UCS-2BE)", "settings.xml");, но все, что делает, - это выводит файл из большого беспорядка (например, http://i.imgur.com/p9cruCf.png или из серии китайских символов), сохраняя при этом кодировку файла в формате ANSI.

Любая идея, как это исправить, или, как альтернатива, как конвертировать файл в UCS-2?

Я новичок в Perl, извините, если что-то из этого не имеет смысла.

РЕДАКТИРОВАТЬ: для тех, кто сталкивается с этой проблемой, пожалуйста, смотрите ответы ниже, они дают подробное объяснение того, как ее исправить.

2 ответа

Решение

XML::Writer не поддерживает ничего, кроме US-ASCII и UTF-8 (как упомянуто в документации его ENCODING аргумент конструктора). Создание XML-документа UCS-2be с использованием XML::Writer сложно, но не невозможно.

use XML::Writer qw( );

# XML::Writer doesn't encode for you, so we need to use :encoding.
# The :raw avoids a problem with CRLF conversion on Windows.
open(my $fh, '>:raw:encoding(UCS-2be)', $qfn)
   or die("Can't create \"$qfn\": $!\n");

# This prints the BOM. It's optional, but it's useful when using an
# encoding that's not a superset of US-ASCII (such as UCS-2be).
print($fh "\x{FEFF}");

my $writer = XML::Writer->new(
   OUTPUT   => $fh,
   ENCODING => 'US-ASCII',   # Use entities for > U+007F
);
$writer->xmlDecl('UCS-2be');
$writer->startTag('root');
$writer->characters("\x{00041}");
$writer->characters("\x{000C9}");
$writer->characters("\x{10000}");
$writer->endTag();
$writer->end();

Недостаток: все символы выше U+007F будут представлены как объекты XML. В приведенном выше примере

  • U+00041 будет присутствовать как "A"(00 41). Хорошо.
  • U + 000C9 будет присутствовать как "É"(00 26 00 23 00 78 00 43 00 39 00 3B). Неоптимальный, но хорошо.
  • U+10000 будет присутствовать как "𐀀"(00 26 00 23 00 78 00 31 00 30 00 30 00 30 00 30 00 3B). Хорошо, сущности XML необходимы для хранения U+10000 с UCB-2e,

Вы можете избежать недостатка, упомянутого выше, если и только если вы можете гарантировать, что писателю не будет предоставлен символ выше U+FFFF.

use XML::Writer qw( );

# XML::Writer doesn't encode for you, so we need to use :encoding.
# The :raw avoids a problem with CRLF conversion on Windows.
open(my $fh, '>:raw:encoding(UCS-2be)', $qfn)
   or die("Can't create \"$qfn\": $!\n");

# This prints the BOM. It's optional, but it's useful when using an
# encoding that's not a superset of US-ASCII (such as UCS-2be).
print($fh "\x{FEFF}");

my $writer = XML::Writer->new(
   OUTPUT   => $fh,
   ENCODING => 'UTF-8',   # Don't use entities.
);
$writer->xmlDecl('UCS-2be');
$writer->startTag('root');
$writer->characters("\x{00041}");
$writer->characters("\x{000C9}");
#$writer->characters("\x{10000}");  # This causes a fatal error
$writer->endTag();
$writer->end();
  • U+00041 будет присутствовать как "A"(00 41). Хорошо.
  • U + 000C9 будет присутствовать как "É"(00 C9). Хорошо.
  • U+10000 вызывает фатальную ошибку.

И вот как вы можете сделать это без каких-либо минусов:

use Encode      qw( decode encode );
use XML::Writer qw( );

my $xml;
{
   # XML::Writer doesn't encode for you, so we need to use :encoding.
   open(my $fh, '>:encoding(UTF-8)', \$xml);

   # This prints the BOM. It's optional, but it's useful when using an
   # encoding that's not a superset of US-ASCII (such as UCS-2be).
   print($fh "\x{FEFF}");

   my $writer = XML::Writer->new(
      OUTPUT   => $fh,
      ENCODING => 'UTF-8',   # Don't use entities.
   );
   $writer->xmlDecl('UCS-2be');
   $writer->startTag('root');
   $writer->characters("\x{00041}");
   $writer->characters("\x{000C9}");
   $writer->characters("\x{10000}");
   $writer->endTag();
   $writer->end();
   close($fh);
}

# Fix encoding.
$xml = decode('UTF-8', $xml);
$xml =~ s/([^\x{0000}-\x{FFFF}])/ sprintf('&#x%X;', ord($1)) /eg;
$xml = encode('UCS-2be', $xml);

open(my $fh, '>:raw', $qfn)
   or die("Can't create \"$qfn\": $!\n");

print($fh $xml);
  • U+00041 будет присутствовать как "A"(00 41). Хорошо.
  • U + 000C9 будет присутствовать как "É"(00 C9). Хорошо.
  • U+10000 будет присутствовать как "𐀀"(00 26 00 23 00 78 00 31 00 30 00 30 00 30 00 30 00 3B). Хорошо, сущности XML необходимы для хранения U+10000 с UCB-2e,

Вы не описываете, что идет не так, но вы можете столкнуться с ошибкой, которую имели некоторые версии perl для Windows с плохим взаимодействием между уровнями кодирования и crlf. Если так, это должно работать:

open(my $output, "> :raw:perlio:encoding(UCS-2BE):crlf:utf8", "settings.xml");

(См. http://www.perlmonks.org/?node_id=608532 для объяснения.)

Если нет, пожалуйста, предоставьте больше информации, чем "все, что делает вывод файла большим беспорядком". Был бы полезен короткий сценарий, демонстрирующий проблему.

Другие вопросы по тегам