Работа с кодировки в Perl

Много въпроси са свързани с разнообразието от кодировки, както и с използваната терминология. Освен това много от нас са се сблъскали с проблеми, свързани с кодирането. Ще се опитам да напиша информация по този въпрос в разбираема форма в тази статия. Ще започна с въпроса за автоматичното определяне на кодирането на текста.

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

В Perl можете да използвате Encode::Guess за това, но по-„напредналата“ индустриална опция е Encode::Detect::Detector. Както е написано в неговата документация, той предоставя интерфейс към универсалния спецификатор на кодиране на Mozil.

Ако изучавате изходния код, обърнете внимание на файла vnsUniversalDetector.cpp и метода

nsresult nsUniversalDetector::HandleData(const char* aBuf, PRUint32 aLen)

От този метод започва цялата работа по определяне на кодирането. Първо се определя дали има заглавка на BOM, ако е така, тогава по-нататъшното кодиране се определя чрез просто сравняване на първоначалните байтове данни:

  • EF BB BF UTF-8 кодирана BOM
  • FE FF 00 00 UCS-4, необичаен ред на октетите BOM (3412)
  • FE FF UTF-16, BOM с голям порядък
  • 00 00 FE FF UTF-32, BOM с голям ред
  • 00 00 FF FE UCS-4, необичаен октетен ред BOM (2143)
  • FF FE 00 00 UTF-32, BOM с малък порядък
  • FF FE UTF-16, BOM с малък ред

По-нататъквсеки байт данни се анализира и дали знакът принадлежи към не-US-ASCII (кодове от 128 до 255) се анализира, ако е така, се създават обекти от клас:

  • nsMBCSGroupProber;
  • nsSBCSGroupProber;
  • nsLatin1Prober;

всяка от които отговаря за анализа на групите за кодиране (MB - многобайтов, SB - единичен байт).

Ако е US-ASCII, тогава има 2 опции тук, или е обикновен ASCII (чист ascii) или файл, съдържащ екраниращи последователности и се отнася до такива кодировки като ISO-2022-KR и т.н. (за повече подробности вижте en.wikipedia.org/wiki/ISO/IEC_2022). В този случай се използва детекторът, реализиран от класа nsEscCharSetProber.

nsMBCSGroupProber поддържа кодировки като: "UTF8", "SJIS", "EUCJP", "GB18030", "EUCKR", "Big5", "EUCTW".

nsSBCSGroupProber - като Win1251, koi8r, ibm866 и други.

Дефиницията на еднобайтово кодиране се основава на анализа на честотата на поява на последователности от 2 знака в текста.

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

Unicode и Perl. Историческа перспектива.Според www.unicode.org/glossary има 7 възможни схеми за кодиране в Unicode: UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE. За самия термин Unicode е дадено следното определение: "... стандарт за цифрово представяне на знаци, които се използват в писмен вид от всички езици на света ...". Освен това има и UTF-7, който не е част от стандарта, но се поддържа от Perl - Encode::Unicode::UTF7 (вижте също RFC 2152).

UTF-7 практически не се използва. Ето какво пише в Encode::Unicode::UTF7 - "...Въпреки това, ако искате да използвате UTF-7 за документи в поща и уеб страници, не го използвайте, докато не сте сигурни, че получателите и читателите (в смисъла на тези документи) могат да се справят с това кодиране ...".

Трябва да се каже, че UTF-8 се използва много широко като кодиране за уеб документи. UTF-16 се използва от Java и Windows, UTF-8 и UTF-32 се използват от Linux и други Unix-подобни системи.

Започвайки с Perl 5.6.0, първоначално беше въведена възможност за работа с Unicode. За по-сериозна работа с Unicode обаче беше препоръчан Perl 5.8.0. Perl 5.14.0 е първата версия, в която поддръжката на Unicode е лесно (почти) интегрируема без няколко клопки (изключение са някои разлики в quotemeta). Версия 5.14 също коригира редица грешки и отклонения от стандарта Unicode.

Visual Studio 2012 и кодировки (за сравнение с Perl).Когато пишем някакво C# приложение в Visual Studio, ние не мислим за кодирането, в което всичко това се съхранява и обработва. Когато създавате документ във Vistual Studio, той ще го създаде в UTF8 и също ще добави UTF8 към заглавката на BOM - последователността от байтове 0xEF, 0xBB, 0xBF. Когато преобразуваме изходния файл (вече отворен във Visual Studio), например, от UTF8 в CP1251, получаваме грешката Някои байтове са заменени със заместващия символ на Unicode при зареждане ... с кодиране Unicode (UTF-8). Запазването на файла няма да запази оригиналното съдържание на файла.

Ако отворите съществуващ файл в cp1251 - ToUpper(), например, ще работи правилно, но ако конвертирате файла в KOI8-R и след това го отворите в Visual Studio и изпълните, почти не е правилноза работа не става дума, тук обкръжението не знае, че е KOI8-R, а как да разбере?

и на изхода те получават, че низовете в променливите не са равни, в началото им е трудно да разберат какво се случва. Подобни неща се случват с регулярни изрази, низови функции (но uc($c) ще работи правилно).

Това е така нареченият "Unicode Bug" в Perl (вижте документацията за повече подробности), свързан с факта, че за различни еднобайтови кодировки символите с кодове от 128 до 255 ще имат различно значение. Например буквата P в cp1251 има код 0xCF, докато в CP866 има код 0x8F, а в KOI8-R има код 0xF0. Как тогава да работим правилно с такива низови функции като uc(), ucfirst(), lc(), lcfirst() или \L, \U в регулярни изрази?

Достатъчно е да "подскажете" на интерпретатора, че кодирането на изходния файл е cp1251 и всичко ще работи правилно. По-точно, в кода по-долу променливите $a и $b ще съхраняват низове във вътрешния формат на Perl.

Вътрешният формат на Perl за низове.В не толкова старите версии на Perl низовете могат да се съхраняват в това, което е известно като вътрешна форма на Perl. Обърнете внимание, че те могат да се съхраняват и просто като колекция от байтове. В примера по-горе, където кодирането на изходния файл не е изрично зададено (с използване на кодиране 'cp1251';), променливите $a, $b, $c просто съхраняват набор от байтове (документацията на Perl също използва термина последователност от октети).

Вътрешният формат се различава от набора от байтове по това, че се използва UTF-8 кодиране и флагът UTF8 е активиран за променливата. Ще ви дам пример. Нека променим малко изходния код на програмата към следния

Ето какво получаваме в резултат

SV = PV(0x199ee4) при 0x19bfb4 REFCNT = 1 ФЛАГИ = (PADMY,POK,pPOK,UTF8) PV = 0x19316c"\321\201\320\273\320\276\320\262\320\276"\0 [UTF8 "\x\x\x\x\x"] CUR = 10 LEN = 12

Обърнете внимание, че FLAGS = (PADMY,POK,pPOK,UTF8). Ако премахнем, използвайте кодиране 'cp1251'; тогава получаваме

SV = PV(0x2d9ee4) при 0x2dbfc4 REFCNT = 1 FLAGS = (PADMY,POK,pPOK) PV = 0x2d316c "\321\201\320\273\320\276\320\262\320\276"\0 CUR = 10 LEN = 12

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

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

Да предположим, че имаме cp866 кодиран файл, който съдържа думата "Кога" (в текстов файл думата Когато е с главна буква). Трябва да го отворим и да анализираме всички редове, за да намерим думата „кога“. Ето как да го направите правилно (докато самият изходен код трябва да е в utf8).

Имайте предвид, че в случай, че не използваме " UTF8 + включва флага UTF8).

Следващият пример получаваме страницата с помощта на LWP::UserAgent. Ето идеален пример как да го направите.

Забележете извикването $content = decode('utf8',$content).

LWP::UserAgent работи с байтове, той не знае и не го интересува в какво кодиране е страницата в cp1251 single-byte или в UTF8, трябва изрично да посочим това. За съжаление, много литература съдържа примери на английски и за по-стари версии на Perl, в резултат на това в тези примери няма нищо за прекодиране.

Използвайки примера за получаване на външни данни от уебсайт, стигнахме доОбмислете използването на модула Encode. Ето неговия основен API, който е много важен за всеки програмист на Perl:

В примера, в който отворихме текстов файл в CP866, можем да пропуснем Tags:

  • Perl
  • кодировки
Добави тагове

Hardcore conf в C++. Каним само професионалисти.