Існує два етапи обробки тексту Unicode. Перший - "як я можу ввести його та вивести його, не втрачаючи інформації". Друга - "як я поводжусь з текстом відповідно до місцевих мовних умов".
Повідомлення tchrist охоплює обидва, але друга частина - це те, звідки походить 99% тексту в його публікації. Більшість програм навіть не обробляють I / O правильно, тому важливо розуміти, що перед тим, як почати турбуватися про нормалізацію та співставлення.
Цей пост має на меті вирішити цю першу проблему
Коли ви читаєте дані в Perl, то все одно, що це кодування. Він виділяє деяку пам’ять і приховує байти туди. Якщо ви говоритеprint $str
, він просто виблискує ці байти до вашого терміналу, який, ймовірно, встановлений, щоб припустити, що все, що йому написано, є UTF-8, і ваш текст відображається.
Чудовий.
За винятком, це не так. Якщо ви спробуєте ставитися до даних як до тексту, ви побачите, що відбувається щось погане. Вам не потрібно йти далі, length
щоб побачити, що те, що Perl думає про вашу струну, і що ви думаєте про свою струну, не погоджуються. Напишіть однолінійку типу: perl -E 'while(<>){ chomp; say length }'
та введіть文字化け
і отримаєте 12 ... не правильна відповідь, 4.
Це тому, що Perl припускає, що ваш рядок не є текстом. Ви повинні сказати йому, що це текст, перш ніж він дасть правильну відповідь.
Це досить просто; Модуль кодування має функції для цього. Загальною точкою входу є 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, і Перл припускав, що все це текст латиниці-1 або двійковий. Отже, коли дані надходять у вашу програму, і ви починаєте трактувати їх як текст, Perl розглядає кожен октет як символ латиниці-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 з пулом у кінці кожного рядка. Ідеально, моя програма працює!
Але ні, ви просто робите бінарне з'єднання. Ви читаєте октети з файлу, видаляєте \n
chomp, а потім торкаєтесь байтів у зображенні PILE OF POO
символу UTF-8 . Переглянувши програму для декодування даних з файлу та кодування виводу, ви помітите, що ви отримуєте сміття ("ð ©") замість poo. Це призведе до того, що ви вважаєте, що розшифрувати вхідний файл - це неправильно. Це не.
Проблема полягає в тому, що поось неявно модернізується як латинь-1. Якщо ви use utf8
зробите буквальний текст замість двійкового, то він спрацює знову!
(Це проблема номер один, яку я бачу, допомагаючи людям з Unicode. Вони зробили частку правильно, і це порушило їхню програму. Ось що сумно в невизначених результатах: ви можете мати робочу програму тривалий час, але коли ви почнете її ремонтувати, Не хвилюйтесь; якщо ви додаєте в програму операції кодування / декодування, і вона порушується, це просто означає, що вам доведеться виконати більше роботи. Наступного разу, коли ви проектуватимете Unicode на увазі з самого початку, це буде набагато простіше!)
Це дійсно все, що вам потрібно знати про Perl та Unicode. Якщо ви скажете Perl, що ваші дані, він має найкращу підтримку Unicode серед усіх популярних мов програмування. Якщо ви припускаєте, що він буде магічно знати, який тип тексту ви його подаєте, тоді ви збираєтеся безповоротно виправити свої дані. Тільки тому, що ваша програма працює сьогодні на вашому терміналі UTF-8, це не означає, що вона працюватиме завтра на закодованому файлом UTF-16. Тож зробіть це безпечним зараз і вбережіть головний біль від збиття даних користувачів!
Легкою частиною обробки Unicode є кодування виводу та декодування вводу. Важкою частиною є пошук усіх ваших вхідних та вихідних даних та визначення того, що це кодування. Але саме тому ви отримуєте великі гроші :)