Почему современный Perl по умолчанию избегает UTF-8?

509

Интересно, почему большинство современных решений, построенных с использованием Perl, не позволяют UTF-8 по умолчанию.

Я понимаю, что для основных сценариев Perl существует много устаревших проблем, где это может нарушить работу. Но, с моей точки зрения, в 21 веке, большие новые проекты (или проекты с большой перспективой) должны сделать свое программное обеспечение UTF-8 доказательством с нуля. Тем не менее я этого не вижу. Например, Moose разрешает строгие и предупреждения, но не Unicode. Modern:: Perl также уменьшает шаблон, но без обработки UTF-8.

Почему? Есть ли некоторые причины, чтобы избежать UTF-8 в современных проектах Perl в 2011 году?


Комментарий @tchrist слишком длинный, поэтому я добавляю его здесь.

Кажется, я не прояснил ситуацию. Позвольте мне попытаться добавить некоторые вещи.

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

tchrist указал так много аспектов, чтобы покрыть, я буду читать и думать о них в течение нескольких дней или даже недель. Тем не менее, это не моя точка зрения. tchrist пытается доказать, что нет единого способа "включить UTF-8". У меня не так много знаний, чтобы спорить с этим. Итак, я придерживаюсь живых примеров.

Я играл с Rakudo, и UTF-8 был там , поскольку мне нужно. У меня не было никаких проблем, это просто сработало. Может быть, есть какое-то ограничение где-то глубже, но в начале все, что я тестировал, работало так, как я ожидал.

Разве это не должно быть целью в современном Perl 5? Я подчеркиваю это больше: я не предлагаю UTF-8 как набор символов по умолчанию для ядра Perl, я предлагаю возможность запускать его с помощью для тех, кто разрабатывает новый проектов.

Другой пример, но с более отрицательным тоном. Рамки должны облегчить процесс разработки. Несколько лет назад я пробовал веб-фреймворки, но просто выбросил их, потому что "включение UTF-8" было настолько неясным. Я не нашел, как и где подключить поддержку Unicode. Это было так много времени, что мне стало легче идти по-старому. Теперь я увидел здесь щедрость, чтобы справиться с той же проблемой с Mason 2: Как сделать Mason2 UTF-8 чистым?. Таким образом, это довольно новая структура, но использование ее с UTF-8 требует глубокого знания ее внутренних компонентов. Это похоже на большой красный знак: STOP, не используйте меня!

Мне очень нравится Perl. Но работать с Юникодом - больно. Я все еще сталкиваюсь с стенами. Какой-то способ tchrist прав и отвечает на мои вопросы: новые проекты не привлекают UTF-8, потому что это слишком сложно в Perl 5.

  • 4
    Привет, ребята, здесь есть несколько флагов, поднятых здесь в этих комментариях. Я сделал снимок комментариев и перенес их в этот чат, где вы можете продолжить обсуждение: chat.stackoverflow.com/rooms/846/…
  • 15
    Извините, но я согласен с @tchrist - UTF-8 чрезвычайно сложен. Нет фреймворка или инструмента, который просто «щелкает выключателем», а затем обрабатывает его правильно Это то, о чем вы должны думать непосредственно при разработке своего приложения, а не то, что любой вид фреймворка или языка может вам помочь. Если Ракудо только что сработал для вас, вы не были достаточно смелыми с вашими контрольными случаями - так как тогда потребуется несколько примеров из ответа @ tchrist и мясника.
Показать ещё 10 комментариев
Теги:
unicode
utf-8

6 ответов

1014
Лучший ответ

?? ???? ??? ?? ????????


????: ???????? ℞: ? ???????? ???????????????

  • Установите PERL_UNICODE в переменную AS. Это заставляет все скрипты Perl декодировать @ARGV как строки UTF-8 и устанавливает кодировку всех трех stdin, stdout и stderr в UTF-8. Оба они являются глобальными эффектами, а не лексическими.

  • В верхней части исходного файла (программа, модуль, библиотека, do hickey), явным образом утверждаю, что вы используете Perl-версию 5.12 или лучше, используя:

    use v5.12; # minimal for unicode string feature

    use v5.14; # optimal for unicode string feature

  • Включить предупреждения, поскольку в предыдущем объявлении разрешены только стриктуры и функции, а не предупреждения. Я также предлагаю продвигать предупреждения Unicode на исключения, поэтому используйте обе эти строки, а не только одну из них. Обратите внимание, однако, что в соответствии с v5.14 класс предупреждения utf8 содержит три других субмашины, которые могут быть включены отдельно: nonchar, surrogate и non_unicode. Возможно, вы захотите усилить контроль.

    use warnings;

    use warnings qw( FATAL utf8 );

  • Объявите, что этот исходный блок закодирован как UTF-8. Хотя когда-то эта прагма делала другие вещи, она теперь служит этой единственной цели в одиночку и никому другом:

    use utf8;

  • Объявить, что все, что открывает дескрипторы файлов в этой лексической области, но не в другом месте, должно предполагать, что этот поток закодирован в UTF-8, если вы не указали это иначе. Таким образом, вы не будете влиять на другие модули или другие программные коды.

    use open qw( :encoding(UTF-8) :std );

  • Включить именованные символы через \N{CHARNAME}.

    use charnames qw( :full :short );

  • Если у вас есть дескриптор DATA, вы должны явно указать его кодировку. Если вы хотите, чтобы это было UTF-8, скажите:

    binmode(DATA, ":encoding(UTF-8)");

Конечно, нет конца другим вопросам, с которыми вы в конечном итоге можете столкнуться, но этого достаточно, чтобы приблизиться к государственной цели: "заставить все работать с UTF-8", хотя и в несколько ослабленном смысле этих сроки.

Еще одна прагма, хотя это не относится к Unicode:

      use autodie;

Настоятельно рекомендуется.


? ? ? ? ? ? ? ? ? ? ? ? ?


Говоря, что "Perl должен [ каким-то образом!] включить Unicode по умолчанию" даже не начинает думать о том, чтобы обойтись, чтобы сказать достаточно, чтобы быть даже незначительно полезным в каком-то редком и изолированном случае, Unicode намного больше, чем просто более крупный репертуар персонажей; его также, как все эти символы взаимодействуют во многих и многих отношениях.

Даже скромные минимальные меры, которые (некоторые) люди, похоже, думают, что они хотят, гарантируют, что они убьют миллионы строк кода, код, который не имеет шансов "обновиться" до вашего нового нового современного храбрых нового мира.

Это намного сложнее, чем люди притворяются. За последние несколько лет я об этом много думал. Мне хотелось бы показать, что я ошибаюсь. Но я не думаю, что это так. Unicode существенно сложнее, чем модель, которую вы хотели бы наложить на нее, и здесь есть сложность, что вы никогда не сможете подметать под ковром. Если вы попробуете, вы сломаете свой собственный код или кого-нибудь. В какой-то момент вам просто нужно сломаться и узнать, что такое Unicode. Вы не можете притворяться, что это не так.

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


????? ??? ? ??????? ⸗ ????? ??????? ???? ????


Как минимум, вот некоторые вещи, которые, по-видимому, потребуются для того, чтобы "включить Unicode по умолчанию", как вы выразились:

  • Все исходные коды должны быть в UTF-8 по умолчанию. Вы можете получить это с помощью use utf8 или export PERL5OPTS=-Mutf8.

  • Ручкой DATA должен быть UTF-8. Вам нужно будет сделать это на основе пакета, как в binmode(DATA, ":encoding(UTF-8)").

  • По умолчанию для аргументов программы для скриптов следует понимать UTF-8. export PERL_UNICODE=A или perl -CA, или export PERL5OPTS=-CA.

  • Стандартные потоки ввода, вывода и ошибок должны иметь значение по умолчанию для UTF-8. export PERL_UNICODE=S для всех из них, или I, O и/или E только для некоторых из них. Это похоже на perl -CS.

  • Любые другие ручки, открытые , должны рассматриваться как UTF-8, если не указано иное; export PERL_UNICODE=D или с I и O для конкретных из них; export PERL5OPTS=-CD будет работать. Это делает -CSAD для всех из них.

  • Накройте обе базы плюс все потоки, которые вы открываете с помощью export PERL5OPTS=-Mopen=:utf8,:std. См. uniquote.

  • Вы не хотите пропустить ошибки кодирования UTF-8. Попробуйте export PERL5OPTS=-Mwarnings=FATAL,utf8. И убедитесь, что ваши входные потоки всегда binmode d до :encoding(UTF-8), а не только :utf8.

  • Кодовые точки между 128-255 должны быть поняты как соответствующие кодовые точки Юникода, а не только непроизведенные двоичные значения. use feature "unicode_strings" или export PERL5OPTS=-Mfeature=unicode_strings. Это сделает uc("\xDF") eq "SS" и "\xE9" =~ /\w/. Простой export PERL5OPTS=-Mv5.12 или лучше также получит это.

  • Именованные символы Unicode по умолчанию не включены, поэтому добавьте export PERL5OPTS=-Mcharnames=:full,:short,latin,greek или некоторые из них. См. uninames и tcgrep.

  • Вам почти всегда нужен доступ к функциям из стандартного модуля Unicode::Normalize различных типов разложений. export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD, а затем всегда запускать входящий материал через NFD и исходящий материал из NFC. Theres нет уровня ввода-вывода для них, но о котором я знаю, но см. nfc, nfd, nfkd и nfkc.

  • Сравнение строк в с использованием eq, ne, lc, cmp, sort, & cc всегда неверно. Поэтому вместо @a = sort @b вам нужно @a = Unicode::Collate->new->sort(@b). Мог бы добавить это к вашему export PERL5OPTS=-MUnicode::Collate. Вы можете кэшировать ключ для двоичных сравнений.

  • встроенные функции, такие как printf и write, поступают неправильно с данными Unicode. Вы должны использовать модуль Unicode::GCString для первого, и оба, а также модуль Unicode::LineBreak для последнего. См. uwc и unifmt.

  • Если вы хотите, чтобы они считались целыми числами, вам придется запускать ваши \d+ снимки через функцию Unicode::UCD::num, потому что встроенный atoi (3) не является в настоящее время достаточно умным.

  • У вас возникнут проблемы с файловой системой в файловых системах. Некоторые файловые системы молча применяют преобразование в NFC; другие молчат принудительное преобразование в НФД. И другие делают что-то еще. Некоторые даже вообще игнорируют этот вопрос, что приводит к еще большим проблемам. Таким образом, вы должны выполнять свою собственную обработку NFC/NFD, чтобы поддерживать работоспособность.

  • Все ваши -коды, содержащие a-z или a-z, и такие ДОЛЖНЫ БЫТЬ ИЗМЕНЕНЫ, включая m//, s/// и tr///. Его должен выделяться как кричащий красный флаг, который нарушает ваш код. Но неясно, как это должно измениться. Получение правильных свойств и понимание их случайных дел сложнее, чем вы думаете. Я использую unichars и uniprops каждый день.

  • Код, который использует \p{Lu}, почти так же неправилен, как и код, который использует [A-Za-z]. Вместо этого вам нужно использовать \p{Upper} и знать причину. Да, \p{Lowercase} и \p{Lower} отличаются от \p{Ll} и \p{Lowercase_Letter}.

  • Код, который использует [A-Za-z], еще хуже. И он не может использовать \pL или \p{Letter}; он должен использовать \p{Alphabetic}. Не все алфавиты - это буквы, вы знаете!

  • Если вы ищете переменные с /[\$\@\%]\w+/, у вас возникнет проблема. Вам нужно искать /[\$\@\%]\p{IDS}\p{IDC}*/, и даже это не думает о переменных препинания или переменных пакета.

  • Если вы проверяете пробелы, вы должны выбрать между \h и \v, в зависимости. И вы никогда не должны использовать \s, так как НЕ ЗНАЕТ [\h\v], вопреки распространенному мнению.

  • Если вы используете \n для границы строки или даже \r\n, то вы делаете это неправильно. Вы должны использовать \R, что не то же самое!

  • Если вы не знаете, когда и следует ли вызывать Unicode::Stringprep, вам лучше научиться.

  • Нечувствительные к регистру сравнения должны проверять, являются ли две вещи одинаковыми буквами независимо от их диакритики и т.д. Самый простой способ сделать это с помощью стандартного Unicode:: Collate. Unicode::Collate->new(level => 1)->cmp($a, $b). Существуют также методы eq и т.д., И вам, вероятно, следует узнать о методах match и substr. Они имеют определенные преимущества перед встроенными встроенными модулями.

  • Иногда этого все еще недостаточно, и вам нужен модуль Unicode:: Collate:: Locale, как в Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b) вместо. Предположим, что Unicode::Collate::->new(level => 1)->eq("d", "ð") истинно, но Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð") является ложным. Аналогичным образом, "ae" и "æ" являются eq, если вы не используете локали или используете английский, но они отличаются в исландском языке. Что теперь? Я говорю вам жестко. Вы можете играть с ucsort, чтобы проверить некоторые из этих вещей.

  • Рассмотрим, как сопоставить шаблон CVCV (согласный, гласный, согласный, гласный) в строке "niño". Его форма NFD, которую вы чертовски лучше, вспомнила, чтобы положить ее - становится "nin\x {303} o". Теперь, что ты собираешься делать? Даже притворяясь, что гласный [aeiou] (что не так, кстати), вы не сможете сделать что-то вроде (?=[aeiou])\X), потому что даже в NFD кодовая точка типа ø не разлагается! Тем не менее, он будет проверяться равным "o, используя сравнение UCA, которое я только что показал вам. Вы не можете полагаться на NFD, вы должны полагаться на UCA.


? ? ? ? ? ? ? ? ? ? ? ? ? ? ?


И это не все. Есть миллионы ошибочных предположений, которые люди делают о Unicode. Пока они не поймут эти вещи, их код будет разорван.

  • Код, предполагающий, что он может открыть текстовый файл без указания того, что кодировка не работает.

  • Код, который предполагает, что кодировка по умолчанию является некоей родной кодировкой платформы.

  • Код, предполагающий, что веб-страницы на японском или китайском языке занимают меньше места в UTF-16, чем в UTF-8, неверны.

  • Код, предполагающий, что Perl использует UTF-8 внутренне неправильно.

  • Код, предполагающий, что ошибки кодирования всегда будут создавать исключение, неверно.

  • Код, предполагающий, что кодовые точки Perl ограничены 0x10_FFFF, неверны.

  • Код, предполагающий, что вы можете установить $/ на то, что будет работать с любым допустимым разделителем строк.

  • Код, который предполагает равенство округления в casefolding, например lc(uc($s)) eq $s или uc(lc($s)) eq $s, полностью нарушен и неверен. Учтите, что uc("σ") и uc("ς") оба "Σ", но lc("Σ") не может вернуть оба из них.

  • Код, предполагающий, что каждая строка в нижнем регистре имеет отдельный верхний регистр или наоборот. Например, "ª" является строчной буквой без прописных букв; тогда как "ᵃ" и "ᴬ" являются буквами, но они не являются строчными буквами; однако они оба являются строчными кодовыми точками без соответствующих версий в верхнем регистре. Понял? Они не \p{Lowercase_Letter}, несмотря на то, что оба являются \p{Letter} и \p{Lowercase}.

  • Код, который предполагает изменение случая, не изменяет длину строки.

  • Код, предполагающий наличие только двух случаев. Theres также titlecase.

  • Код, предполагающий, что только буквы сломаны. Оказывается, что за пределами букв цифры, символы и четные знаки имеют дело. Фактически, изменение случая может даже заставить что-то изменить свою основную общую категорию, например, \p{Mark}, превратившуюся в \p{Letter}. Он также может заставить его переключиться с одного script на другой.

  • Код, предполагающий, что случай никогда не зависит от локали.

  • Код, который предполагает, что Unicode показывает, что локали POSIX нарушены.

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

  • Код, предполагающий, что диакритические знаки \p{Diacritic} и метки \p{Mark} - это одно и то же, что сломано.

  • Код, который предполагает \p{GC=Dash_Punctuation}, охватывает как \p{Dash}.

  • Код, который предполагает, что тире, дефисы и минусы являются одними и теми же, что и друг у друга, или что есть только один, разбит и ошибочен.

  • Код, предполагающий, что каждая точка кода занимает не более одного столбца печати. ​​

  • Код, предполагающий, что все символы \p{Mark} занимают нулевые столбцы печати, сломаны.

  • Код, предполагающий, что одинаковые одинаковые символы нарушены.

  • Код, предполагающий, что символы, которые не похожи друг на друга, не похожи друг на друга.

  • Код, предполагающий, что существует ограничение на количество кодовых точек в строке, которые могут совпадать только с одним \X.

  • Код, который предполагает \X, никогда не может начинаться с символа \p{Mark}.

  • Код, предполагающий, что \X не может содержать два символа не \p{Mark}.

  • Код, предполагающий, что он не может использовать "\x{FFFF}", неверен.

  • Код, предполагающий кодовую точку, отличную от BMP, которая требует, чтобы два блока кода UTF-16 (суррогатные) кодировали два отдельных символа UTF-8, по одному на единицу кода, ошибочны. Он не делает: он кодирует единую кодовую точку.

  • Код, который перекодирует из UTF-16 или UTF-32 с ведущими спецификациями в UTF-8, прерывается, если он помещает спецификацию в начале результирующего UTF-8. Это настолько глупо, что у инженера должны быть сняты веки.

  • Код, предполагающий, что CESU-8 является допустимой кодировкой UTF, неверен. Аналогично, код, который считает кодирование U + 0000 как "\xC0\x80", является UTF-8, сломан и неверен. Эти парни также заслуживают лечения век.

  • Код, который предполагает, что символы, такие как >, всегда указывают вправо, а < всегда указывает на то, что они ошибочны, потому что на самом деле этого не делают.

  • Код, который предполагает, что сначала выведите символ X, а затем символ Y, который будет отображаться как XY. Иногда они не делают.

  • Код, предполагающий, что ASCII достаточно хорош для правильного написания английского языка, является глупым, близоруким, неграмотным, сломанным, злым и неправильным. С головы! Если это кажется слишком экстремальным, мы можем пойти на компромисс: отныне они могут печатать только с большим пальцем ноги с одной ноги (остальное все еще уклоняется).

  • Код, предполагающий, что все теги \p{Math} являются видимыми символами, неверны.

  • Код, который предполагает \w, содержит только буквы, цифры и символы подчеркивания.

  • Код, предполагающий, что ^ и ~ являются знаками пунктуации.

  • Код, предполагающий, что ü ошибочно принят umlaut.

  • Код, который считает, что такие вещи, как , содержат любые буквы в них.

  • Код, который считает, что \p{InLatin} совпадает с \p{Latin}.

  • Код, который считает, что \p{InLatin} почти всегда полезен, почти наверняка ошибочен.

  • Код, который считает, что данный $FIRST_LETTER как первая буква в некотором алфавите и $LAST_LETTER как последняя буква в том же алфавите, что [${FIRST_LETTER}-${LAST_LETTER}] имеет какой-либо смысл, почти всегда полный сломанный и неправильный и бессмысленны.

  • Код, который считает, что имя someones может содержать только определенные символы, является глупым, оскорбительным и неправильным.

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

  • Код, который считает, что какой-то способ притворяться, что кодировки текстового файла не существуют, сломаны и опасны. Мог бы тоже высунуть другой глаз.

  • Код, который преобразует неизвестные символы в ?, является нарушенным, глупым, braindead и работает вопреки стандартной рекомендации, в которой говорится, что НЕ ДОЛЖЕН ЭТО! RTFM, почему бы и нет.

  • Код, который считает, что он может надежно угадать кодировку немаркированного текстового файла, виноват в фатальной смелости высокомерие и наивности, которые исправит только молния от Зевса.

  • Код, который считает, что вы можете использовать ширину printf для прокладки и оправдывать данные Unicode, не работает и не работает.

  • Код, который верит, как только вы успешно создадите файл по указанному имени, что при запуске ls или readdir в его вложенном каталоге вы действительно найдете, что файл с именем, созданным вами под ошибкой, сломанный и неправильный. Не удивляйтесь этому!

  • Код, который считает, что UTF-16 является кодировкой с фиксированной шириной, является глупым, сломанным и неправильным. Отмените свою лицензию на программирование.

  • Код, который обрабатывает кодовые точки с одной плоскости, отличающейся от той, что находится в любой другой плоскости, ipso facto сломан и неверен. Вернитесь в школу.

  • Код, который считает, что такие вещи, как /s/i, могут соответствовать только "S" или "S". Вы будете удивлены.

  • Код, который использует \PM\pM* для поиска кластеров grapheme вместо использования \X, разбит и ошибочен.

  • Людям, которые хотят вернуться в мир ASCII, следует всемерно поощрять это делать, и в честь их славного обновления они должны быть предоставлены бесплатно с помощью электроприводной ручной пишущей машинки для всех своих данных, потребностям. Сообщения, отправленные им, должны быть отправлены через телеграф "ᴀʟʟᴄᴀᴘs" по 40 символов в строке и доставлены вручную курьером. СТОП.


??????⸗????? ??? ???????⸗????? ????


Мой собственный шаблон в наши дни имеет тенденцию выглядеть так:

use 5.014;

use utf8;
use strict;
use autodie;
use warnings; 
use warnings    qw< FATAL  utf8     >;
use open        qw< :std  :utf8     >;
use charnames   qw< :full >;
use feature     qw< unicode_strings >;

use File::Basename      qw< basename >;
use Carp                qw< carp croak confess cluck >;
use Encode              qw< encode decode >;
use Unicode::Normalize  qw< NFD NFC >;

END { close STDOUT }

if (grep /\P{ASCII}/ => @ARGV) { 
   @ARGV = map { decode("UTF-8", $_) } @ARGV;
}

$0 = basename($0);  # shorter messages
$| = 1;

binmode(DATA, ":utf8");

# give a full stack dump on any untrapped exceptions
local $SIG{__DIE__} = sub {
    confess "Uncaught exception: @_" unless $^S;
};

# now promote run-time warnings into stackdumped exceptions
#   *unless* we're in an try block, in which 
#   case just generate a clucking stackdump instead
local $SIG{__WARN__} = sub {
    if ($^S) { cluck   "Trapped warning: @_" } 
    else     { confess "Deadly warning: @_"  }
};

while (<>)  {
    chomp;
    $_ = NFD($_);
    ...
} continue {
    say NFC($_);
}

__END__

? ? ? ? ? ? ? ?


Я не знаю, сколько еще "по умолчанию Unicode в " вы можете получить, чем то, что написал Ive. Ну, да, я тоже: вы должны использовать Unicode::Collate и Unicode::LineBreak тоже. И, вероятно, больше.

Как вы видите, в Unicode слишком много вещей, о которых вам действительно нужно беспокоиться, чтобы когда-либо существовала такая вещь, как "по умолчанию для Unicode".

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

И даже после того, как вы это сделаете, все еще существуют важные проблемы, требующие большой мысли, чтобы получить право. Нет переключателя, который вы можете перевернуть. Ничего, кроме мозга, и я имею в виду настоящий мозг, хватит здесь. Theres чертовски много материала, который вы должны изучить. Подойдя к ручному пишущей машинке, вы просто не можете надеяться прокрасться по незнанию. Это 21 век, и вы не можете пожелать Юникоду умышленным невежеством.

Вы должны это изучить. Период. Никогда не будет так просто, что "все просто работает", потому что это гарантирует, что многие вещи dont работают, что делает недействительным предположение, что когда-либо может быть "заставить все работать",

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

Как только один пример, каноническое упорядочение вызовет некоторые реальные головные боли. "\x{F5}" , "o\x{303}" ' õ, "o\x{303}\x{304}" и "o\x{304}\x{303}" ' ō должны соответствовать , но как в мире вы собираетесь это сделать? Это сложнее, чем кажется, но это то, что вам нужно учитывать.

Если я знаю только о Perl, это то, что делают его биты Unicode и не делают, и эта вещь, которую я обещаю вам: "ᴛʜᴇʀᴇ ɪs ɴᴏ Uɴɪᴄᴏᴅᴇ ᴍᴀɢɪᴄ ʙᴜʟʟᴇᴛ"

Вы не можете просто изменить некоторые значения по умолчанию и получить плавный переход. Это правда, что я запускаю с PERL_UNICODE, установленным на "SA", но все это, и даже это в основном для командной строки. Для настоящей работы я прохожу через все описанные выше многочисленные шаги, и я делаю это очень внимательно ** тщательно.


ƨdləɥ ƨᴉɥʇ ədoɥ puɐ'λɐp əɔᴉu ɐ əʌɐɥ'ʞɔnl poo⅁

  • 55
    Как указал Шерм Пендли: «Все!». Если я сегодня напишу что-то новое, UTF-8 должен стать самым простым способом добиться цели. Это не. Ваш шаблон доказывает это. Не у всех есть такие знания, чтобы повернуть столько тумблеров в правильное положение. Извините, у меня был длинный и тяжелый день, поэтому я прокомментирую в основной записи завтра больше с примерами.
  • 13
    @wk: Так это круто, что код вроде perl -i.bak -pe 's/foo/bar' ломается? Это чертовски много в мире. Какое сравнение вы хотите для eq ? А UCA3 сравнить? Есть ли lc превратить его в UCA1? Как ты можешь знать? Как вы будете сопоставлять частичные и / или несмежные глифы? Это нормально, что весь старый код с 8-битными данными в нем теперь не компилируется? Это нормально, что Perl больше не работает с двоичными данными? Это нормально, чтобы получить разные ответы? Это нормально надувать az из - под людьми без их согласия? Можно ли разбивать графемы? Допустимо ли замедление в 100 раз в коде сортировки? Как насчет файловой системы?
Показать ещё 44 комментария
87

Существует два этапа обработки текста Юникода. Во-первых, "как я могу ввести его и вывести его без потери информации". Во-вторых, "как обрабатывать текст в соответствии с соглашениями на локальном языке".

tchrist post охватывает оба, но вторая часть - это то, откуда приходит 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, на которые работает ваша программа, на октеты, которые могут использоваться внешним миром. Было бы намного проще, если бы мы могли просто отправлять персонажей через Интернет или на наши терминалы, но мы не можем: октеты. Поэтому нам нужно преобразовать символы в октеты, иначе результаты будут undefined.

Подводя итог: закодируйте все выходы и декодируйте все входы.

Теперь мы поговорим о трех проблемах, которые делают это немного сложной задачей. Первая - это библиотеки. Правильно ли они обрабатывают текст? Ответ... они пытаются. Если вы загрузите веб-страницу, 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 обрабатывает The Past. Давным-давно, не было такой вещи, как Unicode, и Perl предполагал, что все было латинским-1 текстом или двоичным. Поэтому, когда данные поступают в вашу программу, и вы начинаете рассматривать ее как текст, Perl обрабатывает каждый октет как символ Latin-1. Поэтому, когда мы попросили длину "文字 化 け", мы получили 12. Перл предположил, что мы работаем на латинской строке "æååã" (которая составляет 12 символов, некоторые из которых не печатаются).

Это называется "неявным обновлением", и это вполне разумная вещь, но это не то, что вы хотите, если ваш текст не является латинским-1. Вот почему критически важно явно декодировать ввод: если вы этого не сделаете, Perl будет, и он может сделать это неправильно.

Люди сталкиваются с проблемами, когда половина их данных является правильной символьной строкой, а некоторые по-прежнему являются двоичными. Perl будет интерпретировать часть, которая по-прежнему двоичная, как если бы она латинско-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, чтобы сделать буквальный текст вместо двоичного, тогда он снова будет работать!

(Это проблема номер один, которую я вижу, когда помогаю людям с Юникодом. Они действительно правы, и это нарушило их программу. Что грустно в результатах undefined: у вас может быть рабочая программа в течение длительного времени, но когда вы начните ремонтировать его, он сломается. Не беспокойтесь: если вы добавляете в программу программы кодирования/декодирования, и это ломается, это просто означает, что у вас есть больше работы. В следующий раз, когда вы разрабатываете с Unicode в виду из начало, это будет намного проще!)

Это действительно все, что вам нужно знать о Perl и Unicode. Если вы сообщите Perl, что ваши данные, у него лучшая поддержка Unicode среди всех популярных языков программирования. Если вы предположите, что это будет волшебно знать, какой текст вы его кормите, однако, вы собираетесь бесполезно уничтожить свои данные. Просто потому, что ваша программа работает сегодня на вашем терминале UTF-8, не означает, что она будет работать завтра в кодированном файле UTF-16. Так что сделайте это безопасно сейчас и избавьте себя от головной боли, которая приведет к сбою данных ваших пользователей!

Легкая часть обработки Юникода - это входной и выходной данные кодирования. Жесткая часть - это поиск всех ваших входных и выходных данных и определение их кодировки. Но вот почему вы получаете большие деньги:)

  • 0
    Принцип хорошо объяснен, но практический подход к вводу / выводу отсутствует. Явное использование модуля Encode утомительно и подвержено ошибкам, и это делает чтение кода относительно ввода-вывода действительно болезненным. Уровни ввода / вывода обеспечивают решение, поскольку они прозрачно кодируют и декодируют, где это необходимо. open и binmode допускают их спецификацию, а pragma open устанавливает значения по умолчанию, как рекомендует tchrist в своем ответе.
45

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

В CPAN появился последний модуль, utf8::all, который пытается "включить Unicode. Все это".

Как уже указывалось, вы не можете магически заставить всю систему (внешние программы, внешние веб-запросы и т.д.) использовать Unicode, но мы можем работать вместе, чтобы сделать разумные инструменты, облегчающие простые проблемы. Это причина, по которой мы программисты.

Если utf8:: все не делает то, что, по вашему мнению, должно, пусть улучшит его, чтобы сделать его лучше. Или позвольте создавать дополнительные инструменты, которые в совокупности могут удовлетворить потребности людей как можно лучше.

`

  • 13
    @tchrist Система отслеживания проблем для utf8 :: все здесь. github.com/doherty/utf8-all/issues Они хотели бы услышать ваши предложения.
  • 4
    @Schwern: Да, но не стесняйтесь воровать и ущипнуть от того, что я здесь написал. Честно говоря, я все еще чувствую / изучаю, что можно сделать против того, что должно быть сделано и где. Вот хороший пример разгрузки сортировки: unichars -gs '/(?=\P{Ll})\p{Lower}|(?=\P{Lu})\p{Upper}/x' | ucsort --upper | cat -n | less -r . Точно так же, небольшие шаги предварительной обработки, такие как ... | ucsort --upper --preprocess='s/(\d+)/sprintf "%#012d", $1/ge' тоже может быть очень хорошим, и я бы не хотел принимать за них решения других. Я все еще строю свой набор инструментов Unicode .
31

Я думаю, вы неправильно понимаете Unicode и его отношение к Perl. Независимо от того, каким образом вы храните данные, Unicode, ISO-8859-1 или многое другое, ваша программа должна знать, как интерпретировать байты он получает как вход (декодирование) и как представлять информацию, которую он хочет выводить (кодирование). Неправильно интерпретируйте эту интерпретацию, и вы разбираете данные. В вашей программе нет какой-то волшебной настройки по умолчанию, которая будет рассказывать вещам вне вашей программы, как действовать.

Вы думаете, что это сложно, скорее всего, потому что вы привыкли ко всему, что ASCII. Все, о чем вы должны думать, просто игнорировалось языком программирования и всеми вещами, с которыми ему приходилось взаимодействовать. Если бы все использовало ничего, кроме UTF-8, и у вас не было выбора, UTF-8 был бы таким же простым. Но не все использует UTF-8. Например, вы не хотите, чтобы ваш дескриптор ввода думал, что он получает октеты UTF-8, если это не так, и вы не хотите, чтобы ваши дескрипторы вывода были UTF-8, если считываемая вещь может обрабатывать UTF-8, Perl не имеет возможности узнать об этом. Вот почему вы программист.

Я не думаю, что Unicode в Perl 5 слишком сложный. Я думаю, что это страшно, и люди избегают этого. Там разница. С этой целью я поместил Unicode в Learning Perl, 6th Edition, и там много элементов Unicode в Эффективном программировании на Perl. Вы должны потратить время, чтобы узнать и понять Unicode и как это работает. В противном случае вы не сможете использовать его.

  • 3
    Я думаю, что у вас есть точка зрения: это страшно. Должно ли это быть? Для меня это Юникод, а использовать его в Perl5 нет (я не предполагаю, что ASCII - мой родной язык, по крайней мере, iso8859-4). Я установил Rakudo, и все, что я попробовал с UTF-8 (в этой ограниченной песочнице), работало из коробки. Я что-то пропустил? Я еще раз подчеркиваю: хорошо иметь хорошо настроенную поддержку Unicode, но в большинстве случаев в этом нет необходимости. Чтобы избавиться от страха по теме, один из способов состоит в том, что все много читают, чтобы понять внутреннее. Другое: у нас особая прагма, поэтому use utf8_everywhere делает людей счастливыми. Почему не последний?
  • 3
    Я все еще думаю, что вы упускаете суть. Что сработало? Вам не нужно понимать внутренности. Вы должны понимать внешние аспекты и то, как вы хотите обрабатывать строки, которые имеют разные кодировки и разные представления одних и тех же символов. Прочитайте совет Тома снова. Бьюсь об заклад, большую часть того, что он говорит, вы обнаружите, что Ракудо не справляется с вами.
Показать ещё 5 комментариев
26

При чтении этой темы у меня часто возникает впечатление, что люди используют " UTF-8" как синоним " Unicode". Проведите различие между "Кодовыми точками" Юникода, которые являются расширенным относительным кодом ASCII и различными "кодировками" Unicode. И есть некоторые из них, из которых UTF-8, UTF-16 и UTF-32 являются текущими, а еще несколько устарели.

Пожалуйста, UTF-8 (как и все другие кодировки) существует и имеет значение только для ввода или вывода. Внутренне, поскольку Perl 5.8.1, все строки сохраняются как Unicode "Кодовые точки". Правда, вам нужно включить некоторые функции, которые были любезно рассмотрены ранее.

  • 19
    Я согласен, что люди слишком часто путают Uɴɪᴄᴏᴅᴇ с UTF-8⧸16⧸32, но в корне и критически неверно, что Uɴɪᴄᴏᴅᴇ - это просто некоторый расширенный набор символов относительно ᴀsᴄɪɪ. В лучшем случае это не что иное, как «ɪsɪ ‑ 10646» . Uɴɪᴄᴏᴅᴇ включает в себя гораздо больше : правила для сопоставления, сворачивания падежа, формы нормализации, кластеры графем, разрывы слов и строк, сценарии, числовые эквиваленты, ширины, двунаправленность, варианты глифов, контекстное поведение, локали, регулярные выражения, объединение классов, сотни свойств, & намного больше!
  • 15
    @tchrist: первым шагом является получение данных в вашу программу и из внешнего мира, не разрушая их. тогда вы можете беспокоиться о сортировке, складывании кейсов, вариантах глифов и т. д., детские шаги.
Показать ещё 1 комментарий
9

Там действительно ужасающее количество древнего кода в дикой природе, большая часть его в виде общих модулей CPAN. Я обнаружил, что должен быть достаточно осторожным, чтобы включить Unicode, если я использую внешние модули, на которые это может повлиять, и я все еще пытаюсь идентифицировать и исправлять некоторые ошибки, связанные с Unicode, в нескольких сценариях Perl, которые я использую регулярно (в частности, iTiVo плохо справляется с чем-либо, что не является 7-разрядным ASCII из-за проблем с перекодировкой).

  • 3
    ОПРЕДЕЛИТЬ «включение Unicode».
  • 0
    Я имел в виду использование опции -C чтобы убедиться, что Perl находится на той же странице, что и Unicode, потому что я по-прежнему решаю использовать ISO 8859/1 вместо Unicode, хотя я явно устанавливаю $LANG и $LC_ALL должным образом , (Это может на самом деле отражать ошибки в библиотеках языковых стандартов платформы.) Как бы то ни было, очень досадно, что я не могу использовать iTivo в программах с акцентами в них, потому что скрипты Perl, которые выполняют работу, сбиваются с ошибками преобразования.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню