Работа с кодировки в 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++. Каним само професионалисти.