Perl струны - PullRequest
       32

Perl струны

8 голосов
/ 03 июня 2010

Как строки perl представлены внутренне? Какая кодировка используется? Как правильно обрабатывать разные кодировки?

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

До этого момента я думал о строках perl как о последовательностях байтов, которые вполне подходили для моих задач. Теперь мне нужно выполнить некоторую обработку файла в кодировке 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> и мусор в консоли. Таким образом, я могу заключить, что строки perl имеют понятие «символ», которое может быть «широким» или нет, но при печати эти «широкие» символы представляются в консоли в виде нескольких байтов, а не в виде одного «символа». (Теперь мне интересно, почему весь мой предыдущий опыт работы с бинарными файлами работал так, как я ожидал, что он будет работать без каких-либо «символических» проблем).

Почему тогда я вижу мусор в консоли? Если Perl хранит строки в виде символов в некоторой известной кодировке, я не думаю, что есть большая проблема, чтобы выяснить кодировку консоли и правильно распечатать текст. (Я использую Windows, кстати).

Если Perl хранит строки в виде символьных последовательностей переменной ширины (например, используя ту же кодировку UTF-8), почему это делается так? По моему опыту C, обработка строк - PAIN.

Обновление .

Я использую два компьютера для тестирования, один из которых работает под управлением Windows 7 x64 с установленным английским языковым пакетом, но с русскими региональными настройками (поэтому у меня cp866 как OEM-кодовая страница и cp1251 как ANSI) с ActivePerl 5.10.1 x64; другая работает под управлением Windows XP 32-битная русская локализация с Cygwin Perl 5.10.0.

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

Ответы [ 3 ]

4 голосов
/ 03 июня 2010

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

Перед печатью необходимо закодировать символы обратно в байты.

use Encode;  
utf8::encode($contents);

Существует также кодирование с двумя аргументами для других кодировок, кроме Юникода. (Это предложение звучит слишком много, не так ли?)

Вот хорошая ссылка. (Было бы больше, но это мой первый пост.) Ознакомьтесь также с perlunitut и статьей Юникода о Джоэле о программном обеспечении.

http://www.ahinea.com/en/tech/perl-unicode-struggle.html

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

4 голосов
/ 03 июня 2010

Строки 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.

2 голосов
/ 03 июня 2010

Вы должны указать свои действительные версии Windows и Perl, поскольку это зависит от используемых вами версий и установленных языковых пакетов. В противном случае сначала посмотрите руководство PerlUnicode -

Perl использует логически широкие символы для внутреннего представления строк.

это подтвердит ваши заявления.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...