2010-10-07 3 views
3

У меня есть внешний модуль, который возвращает мне несколько строк. Я не уверен, как точно вернутся строки. Я действительно не знаю, как работают строки Unicode и почему.Unicode string mess в perl

Модуль должен вернуть, например, чешское слово «být», что означает «быть». (Если вы не видите вторую букву, она должна выглядеть как this.) Если я отображу строку, возвращенную модулем, с помощью Data Dumper, я вижу это как b\x{fd}t.

Однако, если я попытаюсь распечатать его с помощью print $s, я получил предупреждение «Широкий символ в печати» и? вместо ý.

Если я попробую Encode::decode(whatever, $s);, результирующая строка не может быть напечатана в любом случае (всегда с предупреждением «Широкий символ», иногда с искалеченными персонажами, иногда справа), независимо от того, что я положил в whatever.

Если я попробую Encode::encode("utf-8", $s);, результирующая строка CAN может быть напечатана без проблем или сообщений об ошибке.

Если я использую use encoding 'utf8';, печать работает без необходимости кодирования/декодирования. Однако, если я использую модуль IO::CaptureOutput или Capture::Tiny, он снова начинает кричать «Широкий персонаж».

У меня есть несколько вопросов, в основном о том, что именно происходит. (Я пытался читать perldocs, но я был не очень мудр из них)

  1. Почему я не могу напечатать строку сразу после ее получения из модуля?
  2. Почему я не могу напечатать строку, декодированную «декодировать»? Что именно «декодировал»?
  3. Что именно «кодировал», и почему не было проблем при печати после кодирования?
  4. Что именно use encoding делать? Почему кодировка по умолчанию отличается от utf-8?
  5. Что мне делать, если я хочу печатать скаляры без каких-либо проблем, даже если я хочу использовать один из модулей захвата?

редактировать: Некоторые люди говорят мне, чтобы использовать -C или binmode или PERL_UNICODE. Это отличный совет. Однако каким-то образом оба модуля захвата волшебным образом разрушают UTF8-версию STDOUT. Кажется, это больше ошибка модулей, но я не уверен.

edit2: Хорошо, лучшим решением было сбросить модули и написать «захват» себя (с гораздо меньшей гибкостью).

+0

Можете ли вы показать пример сценария, который демонстрирует проблему? Есть много штук, чтобы получить право, поэтому легко работать с чем-то конкретным. –

+1

И когда у вас есть сценарий с коротким примером, используйте его, чтобы сообщить об ошибке для этих модулей в [RT] (http://rt.cpan.org). –

ответ

5
  1. Поскольку вы выводите строку во внутренней форме perl (utf8) для дескриптора файла, отличного от юникода.
  2. Функция decode декодирует последовательность байтов, предположительно входящих в ENCODING, во внутреннюю форму Perl (utf8). Ваш вход, кажется, уже декодирован,
  3. Функция encode() кодирует строку из внутренней формы Perl в ENCODING.
  4. Прагма encoding позволяет писать сценарий в любой кодировке, которая вам нравится. Строковые литералы автоматически преобразуются во внутреннюю форму perl.
  5. Убедитесь, что perl знает, какая кодировка ваших данных поступает и выходит.

См. Также perluniintro, perlunicode, Encode module, binmode().

+0

Большое спасибо. Как открыть STDOUT, так что это дескриптор файла Unicode? И почему это не в дефолте? edit: О, хорошо, я вижу, это binmode. Второй «подзапрос» по-прежнему стоит. Почему binmode STDOUT не UTF-8 по умолчанию? –

+1

@Karel Bilek: Вероятно, это не по умолчанию из-за проблем с обратной совместимостью. Perl6 все это правильно. – Daenyth

+0

Нет, к сожалению, это не работает. Функции захвата (оба они) снова делают binmode STDOUT не-utf8, тем самым нарушая его. –

1

Вы также должны посмотреть на PERL_UNICODE environment variable, что аналогично использованию the -C option. Это позволяет вам устанавливать STDIN/STDOUT/STDERR (и @ARGV) как UTF-8 без необходимости изменять ваши скрипты.

+0

Нет. Даже '-C' не выдерживает функции захвата. Но, похоже, это больше проблема с самими функциями, а не с Perl. Я полагаю. –

+0

ОК. Самое простое разрешение ... заключалось в том, чтобы написать функцию самостоятельно (с гораздо меньшей гибкостью). Спасибо :) –

+0

@Karel Bílek, какое значение вы использовали с '-C'? Существует ряд возможных настроек, как описано в документах, с которыми я связан. – cjm

3

Я рекомендую прочитать главу Юникода в моей книге Эффективное программирование на Perl. Мы собрали все документы, которые могли найти и объяснили Unicode в Perl гораздо более согласованно, чем я видел где-то еще.

Эта программа отлично работает для меня:

#!perl 

use utf8; 
use 5.010; 

binmode STDOUT, ':utf8'; 

my $string = return_string(); 

say $string; 

sub return_string { 'být' } 

Кроме того, Capture::Tiny работает просто отлично для меня:

#!perl 
use utf8; 
use 5.010; 
use Capture::Tiny qw(capture); 

binmode STDOUT, ':utf8'; 

my($stdout, $stderr) = capture { 
    system($^X, '/Users/brian/Desktop/czech.pl'); 
    }; 

say "STDOUT is [$stdout]"; 

IO::CaptureOutput, кажется, есть некоторые проблемы, хотя:

#!perl 
use utf8; 
use 5.010; 
use IO::CaptureOutput qw(capture); 

binmode STDOUT, ':utf8'; 

capture { 
    system($^X, '/Users/brian/Desktop/czech.pl'); 
    } \my $stdout, \my $stderr; 

say "STDOUT is [$stdout]"; 

Для это я получаю:

STDOUT is [být 
] 

Однако это легко исправить. Не используйте этот модуль. :)

+0

FWIW, IO :: CaptureOutput «работает» для меня. Но я считаю, что багги и результаты вы получите правильно. qx // работает, как я считаю правильным, давая вам 5 символов (включая новую строку) по умолчанию и 4 символа, если указано 'use open IN =>:: utf8"; '. – ysth