Unicode вопросы обработки Unicode в Perl и как с ним справиться

Perl 5.8 + имеет полную поддержку Unicode и широкий спектр различных кодировок текста. Но все-таки много людей испытывали 
проблемы при работе с многоязычным текстом. Здесь я объясню наиболее общие проблемы и предлагать решения.

Старая версия данной статьи доступна. Это не так хорошо структурирована, но обеспечивает некоторые дополнительные версии Perl 5.6.1 Unicode 
связанных деталей.
    Вы можете прочитать эту часть и погрузиться во все технические детали и особенностям Perl и Unicode. Или вы можете нанять меня ,
чтобы исправить ваш код.

Куча perldoc страницы Справочника план и объяснить, поддержка юникода Perl. perluniintro , perlunicode , Encode модуль, binmode() функцию. 
И этот список не является полным. Основная проблема с этой документации является ее объем. Большинство программистов даже не нужно читать все это,
 потому что для начала работы с Unicode вам просто необходимо знать некоторые основные факты и правила.

Я испытал несколько видов проблемы с Unicode в Perl, в нескольких проектах. Две основные проблемы, которые я видел, являются:

    UTF-8 данные получают двойной кодировке или другие кодировки данных получают искаженное
    "Широкий характер в печати" предупреждение 

Эти две проблемы тесно связаны между собой и часто решаются аналогичные шаги.

Чтение или по крайней мере просматривают связанные страницы Справочника по-прежнему хороший способ понять и решить ваши проблемы Unicode.
Если у вас нет на это времени сейчас, читайте дальше.
Проблема витрина: пример

Представьте себе два простых переменных с Unicode текст в нем. И вы печатаете этих переменных на стандартный вывод. Что может быть проще? ..

     #!/usr/bin/perl my $ustring1 = "Hello \x{263A}!\n"; my $ustring2 = ; print "$ustring1$ustring2"; __DATA__ Hello O! 

источник

И здесь переменные содержат те же данные: строка "Hello " следуют Юникод БЕЛЫЙ улыбающееся лицо U +263, восклицательный знак и символ новой строки.
 _ _ _DATA_ Часть ( $ustring2 ) является UTF-8 кодировке.

Но когда мы печатаем его, первым выходит тонкая, а второй идет искажен. Это потому, что Perl знает, что первая строка Unicode строку и внутренне 
хранятся в кодировке UTF-8. Но он не знает, кодирование секунду. Когда он строит большую строку для печати, перекодирует второй в UTF-8, неправильно.

Кроме того, он печатает предупреждение: Wide character in print at unitest1.pl line 6,  line 1. мы рассмотрим это позже , после того как мы исправить нашей продукции.

Можно по-видимому исправить положение, избегая объединения:

     #!/usr/bin/perl my $ustring1 = "Hello \x{263A}!\n"; my $ustring2 = ; print $ustring1, $ustring2; __DATA__ Hello O! 

источник

Но это не решение Иногда вы просто не можете избежать объединения;. Это такие основные операции. Кроме того, он подвержен ошибкам, а не будущее.
Почему проблема происходит

Во-первых, некоторые основные факты.

Существует distiction между байтами и символами. Символы Unicode символы. Один персонаж может быть представлено несколько байт, при хранении,
печатных или отправлены по сети. Это зависит от определенной кодировке используется. UTF-8 является лишь одним из способов сделать представлять 
данные в формате Юникод.

Perl имеет "utf8" флаг для каждого скалярного значения, которые могут быть "на" или "выключено". "На" состояние флаг говорит Perl для лечения 
значение в виде строки символов Юникода.

Если взять строку с utf8 флаг выходные и объединить его с строку, utf8 флаг, Perl преобразует первым Unicode.

Возможно, это звучит нормально и очевидно. Но тогда вы думаете: как Perl, необходимо знать кодировку строки данных до преобразования его. 
И Perl попытается угадать его. И это обычный источник проблем.

Алгоритм Perl использует при угадать это документально (по умолчанию использует некоторые проверки и, возможно, вашей местности), 
но мое твердое предложение: никогда не позволяйте Perl это сделать. В противном случае, есть большой шанс, что вы получите двойной кодировке
 UTF-8 строк, или иным образом подогнаны данных.

Решение: всегда кодирования данных явным, как для Вашего входа и выхода.
Решение № 1: Преобразование строки в Unicode

Одним из решений может быть сказать, что Perl $ustring2 содержит данных в формате Юникод UTF-8 кодировке. Существует несколько способов сделать это,
православные путь лежит через Кодировать в decode_utf8() функции:

     #!/usr/bin/perl 
	 use Encode; 
	 my $ustring1 = "Hello \x{263A}!\n"; 
	 my $ustring2 = ; 
	 $ustring2 = decode_utf8( $ustring2 ); 
	 print "$ustring1$ustring2"; 
	 __DATA__ Hello O! 


В этом простом случае в обоих направлениях будет делать работу, но может быть довольно утомительным, если импорт многочисленны. 
И он по-прежнему печатает "Широкий характер" предупреждение.

Но это то, что вы всегда должны сделать для международных данных, которые вы получаете от других модулей, как из баз данных.

Не стоит забывать, однако, что не каждая последовательность байтов действительно UTF-8. Так decode_utf8 () операция может завершиться неудачей. 
Смотрите Encode perldoc подробности обработки ошибок.

Еще один способ сделать пусть Perl принимать UTF-8 данные, как таковой, с пакета "U0C *", распаковать "C *" взломать .

Если вы получаете данные в другую кодировку (не UTF-8), преобразует его в Юникод в явном виде. Опять же, Кодировать модуль decode() функции:

     require Encode; 
	 my $ustring = Encode::decode( 'iso-8859-1', $input ); 

Другой пример: UTF-8 данные из CGI

В ACIS мы производим HTML-страниц в кодировке UTF-8. Мы ожидаем, что HTML-форма ввода для UTF-8, а также. Чтобы работать с ним, мы говорим о Perl кодирования:

     require Encode; 
	 require CGI; 
	 my $query = CGI ->new; 
	 my $form_input = {}; 
	 foreach my $name ( $query ->param ) { my @val = $query ->param( $name ); 
	 foreach ( @val ) { $_ = Encode::decode_utf8( $_ ); } 
	 $name = Encode::decode_utf8( $name ); 
	 if ( scalar @val == 1 ) 
	 {
	     $form_input ->{$name} = $val[0];
	 } 
	 else { $form_input ->{$name} = \@val; 
	 # save value as an array ref } } 

Это создает готовый и безопасный в использовании хэш входных параметров.
Решение № 2: Укажите слоев IO кодировки для вашего дескрипторы

В Perl 5.8 дескриптор файла может иметь кодировку, заданную для этого. Perl затем конвертирует все входные из файла автоматически в свою внутреннюю кодировку Юникод. Она будет отмечать значения, прочитанные из его соответствующим образом с utf8 флаг. Равным образом, Perl может преобразовать вывод в определенной кодировке для дескриптор файла. Кроме того, Perl проверяет, что данные, которые вы действительно выход для кодирования в дескриптор файла.

Так что, если вы читаете данные из файла или другого входного потока, и вы ожидаете, UTF-8, данные там, предупреждают Perl:

     if ( open( FILE, "<:utf8", $fname ) ) { . . . } 

или, в случае нашего простого теста,

     #!/usr/bin/perl
	 my $ustring1 = "Hello \x{263A}!\n"; 
	 binmode DATA, ":utf8"; my $ustring2 = ; 
	 print "$ustring1$ustring2"; 
	 __DATA__ Hello O! 

Это должно вывести две равные линии и не делают никаких раздражающих предупреждений .

Аналогично, если вы открываете файл, как:

     open FILE, "<:encoding(iso-8859-7)", $filename; 

это содержание будет считаться в ISO-8859-7 кодирования. Perl будет использовать это, чтобы интерпретировать данные файла правильно, то есть, 
чтобы преобразовать его внутреннюю UTF-8.

Решение № 3: Глобальные настройки Unicode в Perl

И есть еще один способ приблизиться к своему кодирования / проблемы с кодировкой. Именно к команде Perl для лечения всех вашей программы вводе и выводе,
 UTF-8 по умолчанию. -C является Perl выключатель, который позволяет Вам сделать это. Просто положите -CS на Perl в командной строке.

Вместо этого можно использовать PERL_UNICODE переменной окружения. Он должен быть установлен в среду, в которой вы выполняете Perl, например:

     god@world:~$ PERL_UNICODE=S perl script.pl 

Будет ли команда Perl предположить, UTF-8 во всех входных и выходных дескрипторы в сценарии и используются модули, по умолчанию. (К сожалению и вопреки 
моим ожиданиям это не оказывает влияния на специальных данных дескриптор файла. Так что это не решение нашей проблемы сценария витрины.)

Вы также можете указать UTF-8-ности только для вашего стандартного ввода или просто стандартный вывод или просто стандартный поток ошибок. 
Прочитайте раздел о -C в PerlRun для полной информации.

Широкий характер в печати предупреждение

Предупреждения происходит, когда вы выводите Unicode строку, не поддерживающих Юникод дескриптор файла. Что такое «не-юникод дескриптор файла?", 
Спросите вы. Вот один, не Unicode-совместимых IO слой на ней (см. Решение № 2 раздел выше.)

Правильный способ исправить это указать кодировку вывода явно, с binmode () или в открытых () вызова. Например, откройте ваш файл следующим образом:

     open FILE, ">:utf8", $filename; 

Для печати UTF-8 на стандартный вывод (или стандартная ошибка), а в нашем случае, мы делаем:

     #!/usr/bin/perl
	 my $ustring1 = "Hello \x{263A}!\n";
	 binmode DATA, ":utf8"; my $ustring2 = ;
	 binmode STDOUT, ":utf8"; 
	 print "$ustring1$ustring2";
	 __DATA__ Hello O! 


Неправильный путь, чтобы избежать предупреждения, чтобы выключить utf8 флаг на вашем чтобы быть печатных данных. Затем символы превратятся в 
байтах и ??Perl будет толкать их к байт-дескриптора гладко. Но вам не нужно, на самом деле.

С другой стороны, если вы открываете файл, как:

     open FILE, ">:encoding(iso-8859-7)", $filename; 

материал при печати будет выводиться в ISO-8859-7 кодирования, транскодирования автоматически. ISO-8859-7 не Unicode-совместимых набора символов, 
так что вы не сможете вывести Unicode символы на ней без предупреждения.
Правильная стратегия:

Если есть возможность, использовать кодировку Unicode (например, UTF-8) для хранения и обработки данных. Всегда убедитесь, что Perl не знает, 
какую кодировку данных входит и выходит. Убедитесь, что все ваши Unicode содержащих скаляры, имеют флаг utf8. Тогда вы можете смело объединения 
строк. Затем вы можете использовать Unicode связанных регулярных выражений, который дает вам великих держав за международные (многоязычное) обработки текста.

Чтобы добиться этого, вам необходимо знать все способы, получает данные в вашу программу. Как только вы получаете некоторые ввода, пометить его 
как Unicode или преобразовать ее в Unicode и спать спокойно.

Иногда данные поступают в вашу программу уже в Unicode и вы не должны беспокоиться. Например, XML-парсеры вернет вас значения строки с utf8 флаг 
"на". (Если вы делаете что-то странное, как и получать его в первоначальном виде с анализатором, который вы не должны делать в любом случае.) 
В приведенном выше примере мы явно включить юникод символов в строке ( $ustring1 ) и Perl знает ее кодирования.

Но когда вы читаете данные из входного потока, из базы данных или из переменных окружения (например, параметров в CGI), вам нужно сообщить о 
его Perl кодировку.

Использование переменных среды PERL_UNICODE заставить UTF-8 IO слоев на вход и / или вывода дескрипторы. 
Источник статьи