Как делают строки жемчуга, представленные внутренне? Какое кодирование используется? Как я обрабатываю различную кодировку правильно?
Я использовал жемчуг в течение довольно долгого времени, но он не включал большую строковую обработку в различной кодировке, и когда я встретился с незначительной проблемой, которая имела некоторое отношение к кодировке, я обычно обращался к некоторым шаманским действиям.
До этого момента я думал о строках жемчуга как последовательности байтов, которые действительно соответствовали вполне прилично для моих задач. Теперь я должен сделать, некоторая обработка UTF-8 закодировала файл и здесь запускает проблему.
Во-первых, я считал файл в строку как это:
open(my $in, '<', $ARGV[0]) or die "cannot open file $ARGV[0] for reading";
binmode($in, ':utf8');
my $contents;
{
local $/;
$contents = <$in>;
}
close($in);
затем просто распечатайте его:
print $contents;
И я получаю две вещи: предупреждение Wide character in print at <scriptname> line <n>
и мусор в консоли. Таким образом, я могу прийти к заключению, что строки жемчуга имеют понятие "символа", который может быть "широким" или нет, но при печати эти "широкие" символы представлены в консоли как несколько байтов, не как единственный "символ". (Интересно теперь, почему сделал весь мой предыдущий опыт с двоичными файлами, работавшими вполне, как я ожидал, что это будет работать без каких-либо "символьных" проблем).
Почему затем я вижу мусор в консоли? Если жемчуг хранит строки как символ в некотором известном кодировании, я не думаю, что существует большая проблема для обнаружения консольного текста кодирования и печати правильно. (Я использую Windows, BTW).
Если жемчуг хранит строки как последовательности символов переменной ширины (например, использующий ту же кодировку UTF-8), почему это сделано этот путь? На основе моего опыта C, обрабатывающего строки, БОЛЬ.
Обновление.
Я использую два компьютера для тестирования, каждый запускает Windows 7 x64 с английским установленным пакетом языка, но с российскими региональными настройками (таким образом, у меня есть cp866 как кодовая страница OEM и cp1251 как ANSI) с ActivePerl 5.10.1 x64; другой выполняет Windows XP российская локализация на 32 бита с Perl Cygwin 5.10.0.
Благодаря ссылкам теперь у меня есть намного более основательное понимание того, что продолжается и как должны быть сделаны вещи.
Установка utf8 перед чтением из файла - это хорошо, она автоматически декодирует байты во внутреннюю кодировку. (Это также UTF-8, но вам не нужно знать и не следует полагаться на него.)
Перед печатью вам необходимо снова закодировать символы в байты.
use Encode;
utf8::encode($contents);
Существует также форма кодирования с двумя аргументами для кодировок, отличных от Unicode. (В этом предложении слишком много эха, не так ли?)
Вот хорошая ссылка. (Было бы больше, но это мой первый пост.) Также ознакомьтесь с perlunitut и статьей в Юникоде о Джоэле о программном обеспечении.
http://www.ahinea.com/en/tech/perl-unicode-struggle.html
О, и он должен использовать многобайтовые строки, потому что в противном случае это не юникод.
Вы должны указать ваши актуальные версии Windows и Perl, поскольку это действительно зависит от используемых вами версий и установленных языковых пакетов.
В противном случае сначала посмотрите руководство PerlUnicode -
Perl использует логически широкие символы для внутреннего представления строк.
он подтвердит ваши утверждения.
Windows не полностью устанавливает все символы UTF8 - это может быть причиной вашей проблемы. Возможно, вам потребуется установить дополнительный языковой пакет.
Строки Perl хранятся внутри в одной из двух кодировок: либо 8-битная собственная кодировка, ориентированная на байты, либо UTF-8. Для обратной сопоставимости предполагается, что все операции ввода-вывода и строки находятся в собственной кодировке, если не указано иное. Собственная кодировка обычно 8-битный ASCII, но это можно изменить с помощью use locale
.
В вашем примере вы вызываете binmode для своего дескриптора ввода, изменяя его для использования семантики : utf8
. Одним из следствий этого является то, что все строки, считываемые из этого дескриптора, будут закодированы как UTF-8. print
по умолчанию записывает в STDOUT
, а STDOUT
по умолчанию ожидает символов в собственном кодировании.
Perl, пытаясь поступить правильно, позволяет отправлять строку UTF-8 на собственный кодированный вывод, но если к этому дескриптору не прикреплена кодировка, он должен угадать, как выводить многобайтовые символы. и он почти наверняка угадает неверно. Вот что означает предупреждение: многобайтовый символ был отправлен в поток, ожидающий только однобайтовых символов, и в результате этот символ, вероятно, был поврежден при переводе.
В зависимости от того, что вы хотите достичь, вы можете использовать модуль Encode, упомянутый Диланом, для преобразования данных UTF-8 в однобайтовый набор символов, который можно безопасно распечатать, или если вы знаете, что все, что прикреплено к STDOUT
может обрабатывать UTF-8, вы можете использовать binmode (STDOUT, ': utf8');
, чтобы сообщить Perl, что вы хотите, чтобы любые данные, отправляемые в STDOUT
, отправлялись как UTF-8 .