Есть две стадии обработки текста Unicode. Первый - «как я могу его ввести и вывести без потери информации». Второе - «как мне относиться к тексту в соответствии с местными языковыми соглашениями».
пост tchrist охватывает оба, но вторая часть - то, откуда 99% текста в его посте взято. Большинство программ даже не обрабатывают ввод / вывод правильно, поэтому важно понять это, прежде чем вы начнете беспокоиться о нормализации и сопоставлении.
Этот пост призван решить эту первую проблему
Когда вы читаете данные в Perl, не важно, какая это кодировка. Он выделяет некоторую память и хранит байты там. Если вы говоритеprint $str
, он просто сбрасывает эти байты на ваш терминал, который, вероятно, настроен на то, чтобы предполагать, что все, что записано в него, является UTF-8, и ваш текст отображается.
Изумительный.
За исключением того, что это не так. Если вы попытаетесь обработать данные как текст, вы увидите, что происходит что-то плохое. Вам не нужно идти дальше, 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 ... ну ... это не будет правильно обрабатываться пока вы не напишите код для правильной обработки.
Вывод данных обычно проще, но если вы видите «широкие символы в печати», то вы знаете, что где-то испортили кодировку. Это предупреждение означает «эй, вы пытаетесь просочиться 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, это будет намного легче!)
Это действительно все, что вам нужно знать о Perl и Unicode. Если вы скажете Perl, какие у вас данные, у вас будет лучшая поддержка Unicode среди всех популярных языков программирования. Однако, если вы предполагаете, что он будет волшебным образом знать, какой тип текста вы подаете, то вы безвозвратно уничтожите свои данные. То, что ваша программа работает сегодня на вашем терминале UTF-8, не означает, что она будет работать завтра с файлом в кодировке UTF-16. Так что сделайте это сейчас безопасным и избавьте себя от головной боли, связанной с уничтожением данных ваших пользователей!
Простая часть обработки Unicode - это кодирование вывода и декодирование ввода. Сложная часть - найти все ваши входные и выходные данные и определить, какая это кодировка. Но именно поэтому вы получаете большие деньги :)