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

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

Я понимаю, что есть много унаследованных проблем для основных сценариев Perl, где они могут что-то сломать. Но, с моей точки зрения, в 21 100 * * веке большие новые проекты (или проекты с большой перспективой) должны сделать свое программное обеспечение 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. Это было так много времени, что мне было легче идти по старому пути. Теперь я увидел здесь награду за такую ​​же проблему с Мейсон 2: Как сделать Mason2 UTF-8 чистым? . Итак, это довольно новый фреймворк, но для его использования с UTF-8 требуется глубокое знание его внутренних возможностей. Это как большой красный знак: СТОП, не используйте меня!

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

Ответы [ 7 ]

1127 голосов
/ 28 мая 2011

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


????: ???????? : ?100 ???????????????

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

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

    use v5.12;  # minimal for unicode string feature
    use v5.14;  # optimal for unicode string feature
    
  3. Включить предупреждения, поскольку предыдущее объявление включает только ограничения и функции, а не предупреждения.Я также предлагаю превратить предупреждения Unicode в исключения, поэтому используйте обе эти строки, а не одну из них.Однако обратите внимание, что в v5.14 класс предупреждений utf8 содержит три других предупреждения, которые могут быть включены по отдельности: nonchar, surrogate и non_unicode.Этими вы, возможно, захотите осуществлять больший контроль над.

    use warnings;
    use warnings qw( FATAL utf8 );
    
  4. Объявите, что этот блок источника кодируется как UTF ‑ 8.Хотя когда-то давно эта прагма делала другие вещи, теперь она служит одной единственной цели, а не другим:

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

    use open qw( :encoding(UTF-8) :std );
    
  6. Включение именованных символов с помощью \N{CHARNAME}.

    use charnames qw( :full :short );
    
  7. Если у вас есть дескриптор DATA, вы должны явно установить его кодировку.Если вы хотите, чтобы это было UTF-8, то скажите:

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

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

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

      use autodie;

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


? ? ? ? ? ? ? ?10 ? ? ? ? ? ?


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

Даже простейшие минимальные меры, которые, как кажется некоторым людям кажется, они хотят, гарантированно сокрушат миллионы строк кода, код, который имеетнет шансов «обновиться» до вашего элегантного нового Brave New World современности.

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

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


83 ????? ??? ? ??????? ⸗ ????? ? ??????? ???? 10


Как минимум, вот некоторые вещи, которые могут потребоваться для того, чтобы enable «включить Юникод по умолчанию», как вы выразились:

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

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

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

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

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

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

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

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

  9. Именованные символы Юникода по умолчанию не включены, поэтому добавьте export PERL5OPTS=-Mcharnames=:full,:short,latin,greek или что-то подобное. См. Uninames и tcgrep .

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

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

  12. ? встроенные модули, такие как printf и write, делают неправильно с данными Unicode. Вам необходимо использовать модуль Unicode::GCString для первого и то и другое, а также модуль Unicode::LineBreak и для второго. См. UWC и Unifmt .

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

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

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

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

  17. Код, который использует [a-zA-Z], еще хуже. И он не может использовать \pL или \p{Letter}; для этого нужно использовать \p{Alphabetic}. Знаете, не все алфавиты - это буквы!

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

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

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

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

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

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

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


1336 *

, 13 ? ? 13 ? 13 13 13 13 13 13 13 13

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

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

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

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

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

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

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

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

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

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

  10. Код, предполагающий изменение регистра, не меняет длину строки.

  11. Код, который предполагает, что есть только два случая, нарушен. Также есть заглавные буквы.

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

  13. Код, который предполагает, что регистр никогда не зависит от локали, не работает.

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

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

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

  17. Код, который предполагает, что \p{GC=Dash_Punctuation} покрывает столько, сколько \p{Dash} нарушено.

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

  19. Код, который предполагает, что каждая кодовая точка занимает не более одного столбца печати, разбит.

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

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

  22. Код, который предполагает, что символы, которые не выглядят одинаково, не похожи, сломаны.

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

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

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

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

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

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

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

  30. Код, в котором предполагается, что такие символы, как >, всегда указывают вправо, а < всегда указывает на лево, неверны, поскольку на самом деле это не так.

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

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

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

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

  35. Код, который предполагает, что ^ и ~ являются знаками препинания, является неправильным.

  36. Код, который предполагает, что ü имеет умлаут, неверен.

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

  38. Код, который полагает, что \p{InLatin} такой же, как \p{Latin}, является чудовищно нарушенным.

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

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

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

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

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

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

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

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

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

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

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

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

  51. Код, который использует \PM\pM* для поиска кластеров графем вместо использования \X, поврежден и неверен.

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


16 * ??????⸗????? ??? ???????⸗????? ???? ? ?


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

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 по умолчанию в you» вы можетеполучить, что я написал.Ну, да, я делаю: вы должны использовать Unicode::Collate и Unicode::LineBreak тоже.И, вероятно, еще.

Как вы видите, существует слишком много Unicode-вещей, о которых вам действительно нужно приходится беспокоиться, поскольку * существует такая вещь, как«Default to Unicode».

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

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

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

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

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

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


? ¡ƨ dləɥ ƨᴉɥʇ ɥdoɥ puɐ

96 голосов
/ 31 мая 2011

Есть два этапа обработки текста в Юникоде. Первый - «как я могу ввести его и вывести без потери информации». Второе - «как мне относиться к тексту в соответствии с местными языковыми соглашениями».

пост 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 - это кодирование вывода и декодирование ввода. Сложная часть - найти все ваши входные и выходные данные и определить, какая это кодировка. Но вот почему вы получаете большие деньги:)

47 голосов
/ 29 мая 2011

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

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

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

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

`

34 голосов
/ 29 мая 2011

Я думаю, вы неправильно понимаете 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, 6-е издание , и в Effective Programming есть много вещей, связанных с Unicode.Вы должны потратить время, чтобы узнать и понять Unicode и как он работает.В противном случае вы не сможете использовать его эффективно.

28 голосов
/ 30 мая 2011

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

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

10 голосов
/ 28 мая 2011

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

1 голос
/ 14 мая 2018

Вы должны включить функцию Unicode Strings, и это по умолчанию, если вы используете v5.14;

Вы не должны действительно использовать Unicode-идентификаторы esp.для внешнего кода через utf8, поскольку они небезопасны в perl5, только cperl понял это правильно.См., Например, http://perl11.org/blog/unicode-identifiers.html

Относительно utf8 для ваших файловых дескрипторов / потоков: вам необходимо самостоятельно решить кодировку ваших внешних данных.Библиотека не может этого знать, и поскольку даже libc не поддерживает utf8, правильные данные utf8 встречаются редко.Есть больше wtf8, аберрация окон utf8 вокруг.

Кстати: Moose на самом деле не "Modern Perl", они просто взломали имя.Moose идеально подходит для постмодернистского Perl в стиле Ларри Уолла, смешанного с Bjarne Stroustrup, в стиле эклектики с правильным синтаксисом perl6, например, с использованием строк для имен переменных, синтаксиса ужасных полей и очень незрелой наивной реализации, которая в 10 раз медленнееправильная реализация.cperl и perl6 - настоящие современные perls, где форма следует за функцией, а реализация сокращена и оптимизирована.

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