Цялата истина за знамето UTF-8

истина

Често погрешно схващане е, че символните низове, за разлика от байтовите, имат зададен флаг UTF-8.
Много хора предполагат, че ако данните са ASCII-7-битова, тогава флагът UTF-8 просто не е важен.

Всъщност обаче той може да бъде зададен или изключен, както при символите, и абсолютно произволни двоични данни.

Можете да имате Unicode низове с този флаг, с този флаг ясен и можете да имате двоични данни с този флаг и този флаг ясен. Съществуват и други възможности.

Да разгледаме случая, когато данните ASCII-7bit имат зададен флаг UTF-8.

Този код извежда "UTF-8 флаг набор!" Тоест, низът ASCII-7bit получи този флаг, след като операцията за разделяне раздели Unicode низ (с флаг UTF-8) на части. Можем да кажем, че програмистът не контролира дали неговите ASCII данни имат флаг UTF-8 или не, зависи от къде и как са получени данните и какви данни са били до него.

Същият ефект се получава, ако декодирате ASCII-7-битови байтове до ASCII-7-битови символи с помощта на Encode: decode ()

Тези. прекодирането напред-назад не променя данните (това се очаква), но задава флага UTF-8.
(обаче това поведение на decode () противоречи на собствената му документация, което от своя страна противоречи на идеята, че не трябва да има документация или гаранции по отношение на флага utf-8 в ASCII данни)

Причините за появата на флага UTF-8 могат да се обяснят с съображения за ефективност. Прекалено скъпо е да се анализира низа след разделяне, за да се разбере дали се състои само от ASCII символи и дали флагът може да бъде изчистен.

Това поведение на флага UTF-8 е подобно на вирус - той заразява всички данни, с които влиза в контакт.

Да разгледаме случая, когато символи, различни от ASCII, Unicode, нямат флаг UTF-8.

Тоест, извикването на функция на модул на трета страна изчисти флага UTF-8. В същото време линиите със и без знамето се оказаха напълно идентични.
Това може да се случи само с символи> 127 и

Преобразува на място вътрешното представяне на низа от UTF-X в еквивалентната октетна последователност в естественото кодиране (Latin-1 или EBCDIC). Самата логическа последователност от символи е непроменена.

По принцип модулът Digest: SHA документира това поведение, въпреки че не се изисква:

Имайте предвид, че подбраните подпрограми преобразуват безшумно входните данни на UTF-8 в своите
еквивалентна байтова последователност в естественото кодиране (вж. utf8: понижаване). Това
страничен ефект влияе само върху начина, по който Perl съхранява данните вътрешно, но
в противен случай оставя действителната стойност на данните непокътната.

Като цяло, всяка функция на 3-та страна може да понижи низовете, без да го отчита в документацията (или, например, да го прави понякога).

Да разгледаме случая, когато абсолютно произволни двоични данни имат флаг UTF-8.

В резултат на това се оказва, че двоичните данни, след обединяване с ASCII низ, са увеличили вътрешния си размер в байтове (но не в символи) от 4 на 7, но само ако, безсмислено, е зададен флагът UTF-8 ASCII.

Въпреки това, когато сравняваме тези данни помежду си, те са идентични, също така, когато извеждат двата реда във файл, дори без да се посочва кодирането, файловете също са идентични.

По този начин бинарните данни могат да нарастват и да получават флаг UTF-8, докато няма грешка, всички вградени функции на Perl се справят с тях по същия начин, както ако няма флаг (ако има изключения, тогава има грешка в тях).

Всеки друг код на Perl също трябва да обработва такива данни без грешки (освен ако не се опита да анализира вътрешната структура на низа или поне да го анализира правилно)

Всъщност случилото се с двоични данни е аналогично на операцията utf8: надстройка. Данните бяха интерпретирани като латиница-1, преобразувани в UTF-8 и бе зададен флагът UTF-8. Тази операция е обратната utf8: понижаване, описано по-горе. utf8: понижаване може да се направи само с латински-1 символа. И utf8: надстройка могат да бъдат произведени
с всякакви байтове (тъй като всеки байт съответства на символ от Latin-1).

Това може да е важно, ако имате голямо количество двоични данни в паметта. Изобщо не е чудесно, ако 400-мегабайтна капка изведнъж се превърне в 700-мегабайтна, само защото сте добавили един ASCII-7-битов байт с UTF-8 флаг към нея. Добър изход от ситуацията тук са модулни тестове или твърдения по време на изпълнение с проверка на флага UTF-8.

Като цяло е невъзможно да се разграничат байтове от символи

Помислете за задачата: напишете функция за въвеждане на XML, ако XML е байт, погледнете кодирането в маркера "xml" и ги прекодирайте в символи. Ако това вече е символ, не правете нищо.