Есть два этапа обработки текста в Юникоде. Первый - «как я могу ввести его и вывести без потери информации». Второе - «как мне относиться к тексту в соответствии с местными языковыми соглашениями».
пост tchrist охватывает оба, но вторая часть - то, откуда 99% текста в его посте взято. Большинство программ даже не обрабатывают ввод-вывод правильно, поэтому важно понять, что еще до того, как вы начнете беспокоиться о нормализации и сопоставлении.
Этот пост призван решить эту первую проблему
Когда вы читаете данные в Perl, все равно, какая это кодировка. Он выделяет некоторую память и хранит байты там. Если вы говорите print $str
, он просто сбрасывает эти байты на ваш терминал, который, вероятно, настроен на то, чтобы предполагать, что все, что записано в него, является UTF-8, и ваш текст отображается.
Marvelous.
Кроме того, это не так. Если вы попытаетесь обработать данные как текст, вы увидите, что происходит что-то плохое. Вам не нужно идти дальше length
, чтобы понять, что Perl думает о вашей строке и что вы думаете о вашей строке, не согласны. Напишите одну строчку, например: perl -E 'while(<>){ chomp; say length }'
и введите 文字化け
, и вы получите 12 ... не правильный ответ, 4.
Это потому, что Perl предполагает, что ваша строка не является текстом. Вы должны сказать ему, что это текст, прежде чем он даст вам правильный ответ.
Это достаточно просто; модуль Encode имеет функции для этого. Общая точка входа - Encode::decode
(или use Encode qw(decode)
, конечно). Эта функция берет некоторую строку из внешнего мира (то, что мы будем называть «октетами», причудливый способ сказать «8-битные байты») и превращает ее в некоторый текст, который Perl поймет. Первым аргументом является имя кодировки символов, например «UTF-8» или «ASCII» или «EUC-JP». Второй аргумент - это строка. Возвращаемым значением является скаляр Perl, содержащий текст.
(Существует также Encode::decode_utf8
, что предполагает кодировку UTF-8.)
Если переписать наш однострочный:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Мы набираем 文字 化 け и получаем «4» в результате. Успех.
Это решение 99% проблем с Unicode в Perl.
Ключ в том, что всякий раз, когда какой-либо текст попадает в вашу программу, вы должны его декодировать. Интернет не может передавать символы. Файлы не могут хранить символы. В вашей базе данных нет символов. Есть только октеты, и вы не можете рассматривать октеты как символы в Perl. Вы должны декодировать закодированные октеты в символы Perl с помощью модуля Encode.
Другая половина проблемы - получение данных из вашей программы. Это легко; вы просто говорите use Encode qw(encode)
, решаете, в какой кодировке будут находиться ваши данные (UTF-8 для терминалов, которые понимают UTF-8, UTF-16 для файлов в Windows и т. д.), а затем выводите результат encode($encoding, $data)
просто вывести $data
.
Эта операция преобразует символы Perl, над которыми работает ваша программа, в октеты, которые могут использоваться внешним миром. Было бы намного проще, если бы мы могли просто отправлять символы через Интернет или на наши терминалы, но мы не можем: только октеты. Поэтому мы должны конвертировать символы в октеты, иначе результаты не определены.
Подводя итог: закодировать все выходы и декодировать все входы.
Теперь мы поговорим о трех вопросах, которые делают это немного сложным. Первое - это библиотеки. Правильно ли они обрабатывают текст? Ответ ... они пытаются. Если вы загрузите веб-страницу, LWP вернет вам ваш результат в виде текста. Если вы вызываете правильный метод для результата, то есть (и это случается decoded_content
, а не content
, который является просто потоком октетов, который он получил от сервера.) Драйверы базы данных могут быть ненадежными; если вы используете DBD :: SQLite только с Perl, это сработает, но если какой-то другой инструмент поместит текст, хранящийся в вашей базе данных в кодировке, отличной от UTF-8 ... ну ... это не будет правильно обрабатываться пока вы не напишите код для правильной обработки.
OuПередача данных обычно проще, но если вы видите «широкие символы в печати», то вы знаете, что где-то испортили кодировку.Это предупреждение означает «эй, вы пытаетесь просочиться Perl-символы во внешний мир, и это не имеет никакого смысла».Кажется, что ваша программа работает (потому что другой конец обычно корректно обрабатывает необработанные символы Perl), но она сильно повреждена и может перестать работать в любой момент.Исправьте это с помощью явного Encode::encode
!
Вторая проблема - это кодированный код UTF-8.Если вы не скажете use utf8
вверху каждого файла, Perl не будет считать, что ваш исходный код - UTF-8.Это означает, что каждый раз, когда вы говорите что-то вроде my $var = 'ほげ'
, вы впрыскиваете в свою программу мусор, который полностью разрушит все.Вам не нужно «использовать utf8», но если вы этого не сделаете, вы должны не использовать в своей программе не-ASCII-символов.
Третья проблема заключается в том, как Perl обрабатываетПрошлое.Давным-давно не было такого понятия, как Unicode, и Perl предполагал, что все было текстовым или двоичным кодом Latin-1.Поэтому, когда данные поступают в вашу программу и вы начинаете обрабатывать их как текст, Perl обрабатывает каждый октет как символ Latin-1.Вот почему, когда мы спросили длину «文字 化 け», мы получили 12. Perl предположил, что мы работаем со строкой Latin-1 «åååã» (которая состоит из 12 символов, некоторые из которых не печатаются).
Это называется «неявным обновлением», и это вполне разумно, но это не то, что вам нужно, если ваш текст не Latin-1.Вот почему так важно явно декодировать ввод: если вы этого не сделаете, Perl сделает это, и он может сделать это неправильно.
Люди сталкиваются с проблемами, когда половина их данных - это правильная строка символов, а некоторые -все еще бинарный.Perl интерпретирует двоичную часть, как будто это текст Latin-1, а затем объединяет ее с правильными символьными данными.Это будет выглядеть так, как будто правильное обращение с вашими персонажами нарушило вашу программу, но в действительности вы просто недостаточно исправили это.
Вот пример: у вас есть программа, которая читает кодированный в UTF-8 текстфайл, вы добавляете Unicode PILE OF POO
к каждой строке и распечатываете его.Вы пишете это так:
while(<>){
chomp;
say "$_ ?";
}
И затем запускаете на некоторых закодированных данных UTF-8, например:
perl poo.pl input-data.txt
Он печатает данные UTF-8 с poo в концекаждая строка.Отлично, моя программа работает!
Но нет, вы просто делаете двоичную конкатенацию.Вы читаете октеты из файла, удаляете \n
с помощью chomp, а затем добавляете байты в UTF-8-представление символа PILE OF POO
.Когда вы пересматриваете свою программу для декодирования данных из файла и кодирования выходных данных, вы заметите, что вместо poo вы получаете мусор ("ð ©").Это заставит вас поверить, что декодирование входного файла - неправильная вещь.Это не так.
Проблема в том, что poo неявно обновляется как latin-1.Если вы use utf8
сделаете буквальный текст вместо двоичного, тогда он снова будет работать!
(Это проблема номер один, которую я вижу, когда помогаю людям с Unicode. Они правильно расстались, и это сломало их программу.Вот что грустно в отношении неопределенных результатов: у вас может быть работающая программа в течение длительного времени, но когда вы начинаете восстанавливать ее, она ломается. Не волнуйтесь, если вы добавляете операторы кодирования / декодирования в свою программу, и она ломается, онапросто означает, что у вас есть больше работы. В следующий раз, когда вы начнете проектировать с Unicode, это будет намного проще!)
Это действительно все, что вам нужно знать о Perl и Unicode.Если вы скажете Perl, какие у вас данные, у вас будет лучшая поддержка Unicode среди всех популярных языков программирования.Однако, если вы предполагаете, что он будет волшебным образом знать, какой тип текста вы подаете, то вы безвозвратно уничтожите свои данные.То, что ваша программа работает сегодня на вашем терминале UTF-8, не означает, что она будет работать завтра с файлом в кодировке UTF-16.Так что сделайте это сейчас безопасным и избавьте себя от головной боли, связанной с уничтожением данных ваших пользователей!
ЛегкоЧасть обработки Unicode - это кодирование вывода и декодирование ввода. Сложная часть - найти все ваши входные и выходные данные и определить, какая это кодировка. Но вот почему вы получаете большие деньги:)