Pietrzak Roman
yosh (at) 3redants (dot) com
Copyright 2008

3 Red Ants Studio
www.3redants.com


Short how-to about converting sites from ISO-8859-2 (known also as Latin-2) to utf-8


Krótkie how-to o konwersji stron WWW z ISO-8859-2 do utf-8



Na początek - przystępnie wytłumaczę co to jest kodowanie, bo wokoło tematu jest wiele nieporozumień...

Co to jest kodowanie


Komputery operują na liczbach zapisanych w bajtach. Procesory rozumieją i przetwarzają tak naprawdę tylko liczby. Nieważne, czy oglądamy extra grafikę w grze 3D, czy edytujemy dokument w OpenOffice, czy przeglądamy stronę WWW. Dla kompa - to tylko liczby. Liczby są zapisane w bajtach. Bajt składa się bitów i tak dalej... Mniejsza o szczegóły - ważne jest to, że jeden bajt mieści tylko 256 różnych liczb (czyli umownie wartości od 0 do 255). Więcej się po prostu nie da.

Jeśli chcemy użyć kompa do pokazywania/edycji tekstu, to wystarczy wymyślić jakieś proste przyporządkowanie. Na przykład, jeśli umówimy się, że:
A to 1
B to 2
C to 3
i tak dalej...
to mamy proste kodowanie.

Skoro wartości w bajcie jest 256, to można w ten sposób przyporządkować 256 różnych znaków. Jeśli weźmiemy pod uwagę, że znaków jest cała masa (literki małe, duże, cyferki, plusiki, myślniki, przecinki) i do tego są różne w zależności od języka w którym piszemy tekst (niemcy mają umlauty, norwedzy na przykład przekreślone O, a chińczycy mają znaczków kilka tysięcy), to okazuje się, że trzeba kombinować - na przykład łączyć po kilka bajtów w jeden znak.

W rozumieniu tego artykułu kodowanie to sposób przyporządkowania wartości bajtów (i ich ciągów) do konkretnych liter.

...no i łatwo się domyślić, że takich przyporządkowań powstało mnóóóstwo na początku światowej komputeryzacji. Na szczęście ludzie zaczęli się dogadywać i definiować standardy.

Standardy : ASCII


Podstawą jest ASCII - standard który definiuje pierwsze 127 znaków. Można sobie go obejrzeć na przykład na calculli.
ASCII definuje tylko podstawowy zestaw literek, cyferek i znaczków, ograniczając się praktycznie do alfabetu angielskiego (jedyny "ogonek" to Q).

Standardy : ISO-8859-2 i WINDOWS-1250


Kodowanie ISO-8859-2, znane też jako Latin-2 dodaje do 127 znaków ASCII, dodatkowe 128 - uzupełnia ASCII o różnokrajowe znaczki, między innymi polskie.
Kodowanie WINDOWS-1250 - daje dokładnie to samo.
Problem w tym, że 8859-2 i windows-1250 - robią to w zupełnie inny sposób (tzn, te same literki mają inne odpowiadające im kody).
Oczywiście powodem jest to, że po prostu kto inny definiował 8859-2, a kto inny windows-1250.

Oba kodowania dodają tylko po 128 znaczków, czyli razem z ASCII używają jednego bajtu na znak, potrafiąc rozróżnić 255 różnych znaków.
NIE DA się zapisać w tych kodowaniach innych znaków, bez niestandardowych kombinacji.
Czyli - tak naprawdę nie da się tym zapisać nawet wszystkich tekstów europejskich.

Przez lata wiele polskich tekstów i stron WWW powstawało w edytorach, które najlepiej pracowały używając właśnie jednego z tych kodowań.

Standardy : inne


Przez lata pracy z tekstami w różnych krajach, powstały dziesiątki/setki standardów kodowań, załatwiających jakieś problemy danego języka.

Garść przykładów:

  • W polsce poza Latin-2, używano też często standardu Mazovia. Poza tym system operacyjny DOS (przed rokiem 95 właściwie nie było innych systemów używanych powszechnie w polsce na PC-ty) definiował także swoje standardy. Dla zainteresowanych: szukajcie na przykład CP790 CP852

  • Chiny - do zapisu języka "Traditional Chinese" jednym z najpowszechniejszych kodowań stało się Big5. Kod ten zawiera także m.in. znaki używane na Tajwanie (chiński "simplified"), Hong Kongu czy Makau. Z nie-chińskich alfabetów, Big5 koduje na przykład cyrylice (Rosja), czy japońską Kanę. Big5 jest kodowaniem wielobajtowym - to oczywiste, że potrzebuje więcej niż jednego bajta, żeby wszystkie znaki przyporządkować.

  • Zestawy znaków ISO - kilka tak zwanych "stron kodowych", oznaczonych różnymi numerkami (8859-x) - pozwalają zapisać znaki w różnych językach, zdefinowane przez komitet standaryzacyjny.

  • Zestawy znaków Windows-xxxx i CPxxxx - generalnie używane w systemach microsoft-u.

  • Kodowanie DVB - używane przez telewizję cyfrową. Realnie zawiera (używa) ISO, Big5, utf i paru innych. Jednak ciekawostką jest to, że każdy tekst może zawierać informację o użytym kodowaniu - teoretycznie umożliwia nawet zmiane kodowania w częśćiach tekstu. W tym celu zdefiniowany jest zestaw prefixów, określających przełączenie kodowania.


Typowym problemem jest kodowanie dokumentów, czy aplikacji (także stron internetowych), które używają wielu języków jednocześnie.
Za pomocą jednobajtowych kodowań nie da się stworzyć dokumentu w języku polskim, który zawierałby cytaty z norweskiego i rosyjskiego...

Potrzebne jest więc kodowanie dość mocno uniwersalne, aby potrafiło przedstawić te tysiące znaczków z całego świata. Jako taki standard obecnie przyjęło się UNICODE...

Unicode, utf-7, utf-8, utf-16, utf-32


Często miesza się pojęcia "Unicode" z kodowaniami "utf" - a to dlatego, że w praktyce są one mocno powiązane.

Unicode (po polsku "unikod"), jest standardem który z założenia definiuje znaki używane w językach całego świata.
Jest ciągle rozwijany. Unicode w wersji 5.0 koduje 98.884 znaki, definiując ich kształt, ale także cel i kontekst użycia.
Początkowe znaki UNICODE są praktycznie takie same jak te zdefiniowane w ASCII.

utf-7, utf-8, utf-16, utf-32 to sposoby zapisu kodów UNICODE.
Kod oparty na tylu różnych znakach musiałby "poświęcać" 4 bajty na każdy znak. I tak właśnie jest przy utf-32. Utf-32 to po prostu zapisany w 4 bajtach UNICODE.
Pozostałe - utf-7, utf-8, utf-16 opierają się na założeniu, że najczęściej używanym zestawem jest i tak zestaw łaciński, rzadziej pewne dodatkowe, a najrzadziej jakieś wynalazki w stylu indiańskich nutek, dzięki czemu, na zakodowanie początkowych znaków UNICODE zużywa się mniej bajtów niż na te używane względnie rzadko.
Dla zainteresowanych u-te-ef-ami polecam poczytać choćby wikipedię - ten temat nie będzie rozwijany w tym artykule.

Szczególnym przypadkiem kodowania UNICODE jest utf-8. A to dlatego, że pierwsze 128 znaków jest zakodowanych jako zwykłe ASCII.
Krótko: każdy tekst w ASCII jest tekstem w utf-8.
Dodatkowe UNICODE-owe znaki są kodowane większą ilością bajtów w utf-8. Im znak rzadziej używany (statystycznie), tym więcej będzie potrzebował bajtów utf-8 do zakodowania. Znaki z najwyższego zakresu UNICODE wymagają aż 6 bajtów w utf-8.

Na tą chwilę utf-8 stało się chyba najczęściej używanym standardem dla stron internetowych - szczególnie tych wielojęzycznych.

Strona w utf-8 : nagłówek (head)


Aby przeglądarka interpretowała stronę jako utf-8, najlepiej jej to powiedzieć. Kod jest banalny:
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
Wstawiamy to gdzieś w nagłówku (najlepiej wśród innych sekcji "meta"), czyli pomiędzy tagami <head></head>.

Oczywiście dobrze jest upewnić się, że nie została gdzieś inna(stara) definicja
meta http-equiv="content-type"
bo zostawienie dwóch (i to sprzecznych) mija się z celem... a zdarzają się i takie kwiatki.

Strona w utf-8 : treść


Treść dokumentu strony oczywiście musi być w UTF-8. Jeśli zostawimy inne kodowanie, a w nagłówku "wmówimy" przeglądarce, że dokument jest w utf-8, to wyświetlą się nam "krzaczki".

Wskazówki:

  • używaj edytora, który natywnie wspiera UTF-8, a do tego daje Ci pełną kontrolę nad kodowaniem - czyli pokazuje jakie kodowanie jest fizycznie w dokumencie, umożliwia konwersję itp.

  • przy ładowaniu stron FTP-em, sprawdź czy program uploadujący wie o UTF-8 albo przełącz klienta FTP w tryb binarny "binary mode" (dokumenty html są ładowane czasami w trybie tekstowym, co czasem może spowodować problemy).

  • jeśli używasz bazy danych (jak MySQL), upewnij się, że baza pracuje w trybie UTF-8. Ewentualnie będziesz musiał przeprowadzić konwersję.



Strona w utf-8 : automatyczna konwersja plików w PHP


Często wystarczy, że "przejedziesz" wszystkie pliki jakimś automatem zamieniającym ISO-8859-2, czy WINDOWS-1250 na UTF-8.

Przykładowy prosty skrypt konwersji z ISO-8859-2 na UTF-8:
function processDir($dir)
{
    if ($dh = opendir($dir))
    {
        while (($file = readdir($dh)) !== false)
        {
            $ext = substr($file, -4);
            if (
                ($file != 'a.php')
                &&
                    (
                        ($ext == '.php')
                        ||
                        ($ext == '.txt')
                    )
                )
            {
                $txt = file_get_contents($dir.'/'.$file);
                $txt = str_replace(chr(0xB1), 'ą', $txt);
                $txt = str_replace(chr(0xE6), 'ć', $txt);
                $txt = str_replace(chr(0xEA), 'ę', $txt);
                $txt = str_replace(chr(0xB3), 'ł', $txt);
                $txt = str_replace(chr(0xF1), 'ń', $txt);
                $txt = str_replace(chr(0xF3), 'ó', $txt);
                $txt = str_replace(chr(0xB6), 'ś', $txt);
                $txt = str_replace(chr(0xBC), 'ź', $txt);
                $txt = str_replace(chr(0xBF), 'ż', $txt);
                $txt = str_replace(chr(0xA1), 'Ą', $txt);
                $txt = str_replace(chr(0xC6), 'Ć', $txt);
                $txt = str_replace(chr(0xCA), 'Ę', $txt);
                $txt = str_replace(chr(0xA3), 'Ł', $txt);
                $txt = str_replace(chr(0xD1), 'Ń', $txt);
                $txt = str_replace(chr(0xD3), 'Ó', $txt);
                $txt = str_replace(chr(0xA6), 'Ś', $txt);
                $txt = str_replace(chr(0xAC), 'Ź', $txt);
                $txt = str_replace(chr(0xAF), 'Ż', $txt);
                file_put_contents($dir.'/'.$file, $txt);
            }
        }
        closedir($dh);
    }
}

processDir('/twoj/katalog');
processDir('/twoj/katalog/doc');
processDir('/twoj/katalog/txt');
processDir('/twoj/katalog/blah/bleh');
zamienia polskie znaki we wszystkich plikach *.php i *.txt spotkanych w podanych "ręcznie" katalogach na końcu skryptu.
Skrypt jest banalny - postaraj się go zrozumieć, zanim użyjesz, żebyś sobie nie narobił bałaganu.
I bardzo ważne - kopiując skrypt ze strony, zapisz go jako UTF-8 !

Dla konwersji Windows-1250 na utf-8 musisz tylko wyedytować numerki zamienianych znaków (miłej zabawy z tabelkami !).

Strona w utf-8 : mySQL


Jeśli masz pełną kontrolę nad bazą, a Twoje skrypty korzystają z niej w standardowy sposób - czasami wystarczy ją przełączyć na pracę w UTF-8, zmieniając konfigurację... i już.

Jednak pewnie napotkasz takie czy inne problemy, związane z prawami dostępu (serwer mySQL-a dzielony na wielu userów), formatowaniem zapytań przy dostępie do bazy itp.
Nie jestem w stanie podać pełnego zestawu rozwiązań - bo niektóre przypadki wymagają dość indywidualnego podejścia, jeśli trzymane w bazie dane są specyficzne.

Garść podstawowych wskazówek:

  • powiedz serwerowi, że preferujesz utf-a. Zaraz po połączeniu (po zawołaniu funkcji mysql_select_db), odpal kod:
    mysql_query('SET character_set_connection=utf8') or die('character_set_connection failed');
    mysql_query('SET character_set_client=utf8') or die('character_set_client failed');
    mysql_query('SET character_set_results=utf8') or die('character_set_results failed');


  • tekstowe kolumny w tabelach deklaruj jako utf-owe (już podczas tworzenia tabeli).
    W tym celu przy każdym polu w definicji, podczas tworzenia dodaj
    character set utf8

    Na przykład:
    CREATE TABLE properties (path text character set utf8 not null, value text character set utf8, primary key (path(32)))


  • pilnuj właściwego kodowania przy dostępie (zapisie/odczycie) do bazy

  • aby przeprowadzić konwersję na istniejącej już bazie, często wystarcza export za pomocą phpMyAdmin-a, i ponowny import z właściwymi ustawieniami.



O kopiowaniu


Ten artykuł może być kopiowany w inne miejsca, po uprzednim uzyskaniu zgody autora.
Artykuł powinien być kopiowany W CAŁOŚCI (z rysunkami) oraz powinien zawierać link do www.3redants.com (z dopiskiem, że stąd pochodzi).