JustPaste.it

Iconv - na bolączki z kodowaniem

Nieco przydługi wstęp o kodowaniu


Nasz język bazuje na alfabecie łacińskim. Posiadamy także kilka liter, tzw. znaków diakrytycznych, które są modyfikacjami łacińskich liter. Dostawiamy ogonek, kreseczkę czy kropkę i mamy inną literę. Pojawia się przez to problem. Komputery to, w dużej mierze, zasługa inżynierów pracujących w Stanach Zjednoczonych. Język angielski nie posługuje się literami spoza alfabetu łacińskiego dlatego litery nie występujące w alfabecie łacińskim zostały zepchnięte na bok. W kodzie ASCII do dyspozycji jest 128 pozycji (siedem bitów). W tych 128 pozycjach należało zmieścić wszystkie litery alfabetu łacińskiego (zarówno wielkie jak i małe), wszystkie cyfry, wszystkie znaki, które widzimy na klawiaturach oraz kilkanaście znaków specjalnych. W sam raz na 128 pozycji. Nie było miejsca na zmieszczenie dodatkowych znaków, szczególnie występujących w tak mało popularnych językach jak polski.

Jednak oryginalnych siedem bitów to było za mało. Przyjęło się, że jeden bajt to osiem bitów i taką właśnie jednostką posługują się komputery. Oczywistym był fakt, żeby każdy znak był reprezentowany przez jeden bajt. W przypadku siedmiobitowego kodu ASCII pozostawał jeden wolny bit. Wykorzystano go, dzięki czemu w jednym bajcie można było zmieścić 256 znaków. Wśród tych znaków znalazło się miejsce dla znaków spoza alfabetu łacińskiego. Ale pojawił się problem. Dodatkowych miejsc było tylko 128, a języków z dodatkowymi znakami dużo więcej... Posłużono się faktem, że większość osób pisze w jednym języku, przez co wykorzystują tylko jeden zestaw znaków dodatkowych. Wymyślono różne kodowania, w których odpowiednie pozycje były obsadzone przez dodatkowe znaki. Grupy języków otrzymały własne kodowania. Interpretacja tekstu wedle innego kodowania powodowała wyświetlanie "krzaczków".

Język polski znalazł się na niezbyt interesującej pozycji. Każdy tworzył sobie własny standard kodowania. W systemach DOS mieliśmy inne, w Windows inne, w Uniksach jeszcze inne. Pojawiały się również edytory tekstu, które stosowały swój własny sposób kodowania znaków. Im więcej kodowań, tym więcej kłopotów. Póki tworzyliśmy pliki na swój własny użytek (ewentualnie wąskiej grupy odbiorców) nie było większych problemów. Jednak gdy pojawił się Internet... Każdy kodował tak jak uznał to za stosowne, a to jego odbiorcy musieli się martwić o poprawną interpretację. Właśnie dlatego rozpoczęła się popularyzacja standardu ISO-8859-2 - standardu międzynarodowego opisującego kodowanie znaków języków środkowoeuropejskich (w tym polskiego). Polski Komitet Normalizacyjny jest jednym z członków założycieli Międzynarodowej Organizacji Normalizacyjnej - ISO, więc siłą rzeczy standard ten obowiązuje również na terenie naszego kraju.

I wszystko byłoby dobrze, gdyby nie Microsoft. Microsoft na potrzeby swojego systemu operacyjnego zaadoptował kodowanie ANSI-1250, stworzone przez Amerykański Narodowy Instytut Standaryzacyjny, którego postanowienia nie mają mocy prawnej poza terenem Stanów Zjednoczonych. Popularność ich systemu operacyjnego sprawiła, że to kodowanie (znane również jako Windows-1250) stało się równie popularne. Tyle popularne, co zwodnicze... Kodowanie to różni się od ISO-8859-2 kilkoma znakami - kilkoma znakami interpunkcyjnymi i trzema literami w obu wariantach (wielkich i małych): ą, ś, ź, Ą, Ś, Ź. Oprogramowanie pod Windows w większości przypadków nasze polskie znaki narodowe koduje właśnie w tym kodowaniu, co oczywiście skutkuje wyświetlaniem "krzaczków" u osób, oczekujących kodowania ISO-8859-2.

Przez ten cały bałagan oraz dzięki faktowi, że ludzie jednak coraz częściej piszą dokumenty w wielu językach, wymyślono kodowanie uniwersalne - Unikod. Podstawowym założenie Unikodu było wykorzystanie więcej niż jednego bajtu do zapisu znaku, dzięki czemu niesamowicie zwiększała się ilość znaków możliwych do zakodowania i rozwiązywał się problem wykorzystania jednej pozycji dla wielu znaków w zależności od kodowania. Mamy dwa popularne warianty Unikodu: UTF-16 i UTF-8. Pierwszy z nich wykorzystuje do każdego znaku dwa bajty, przez co tekst napisany w tym kodowaniu zajmuje dwa razy więcej niż tekst napisany w jakimkolwiek kodowaniu ośmiobitowym. Drugi wariant, najczęściej spotykany, to UTF-8, który znaki występujące w ASCII koduje przy pomocy jednego bitu (zgodność z ASCII), a do zakodowania innych znaków wykorzystuje dwa, czasem więcej bitów. Dzięki temu nie jest tak "nadmiarowy" jak UTF-16 i sprawuje się wyśmienicie.

Uniwersalny konwerter - iconv


W środowiskach uniksowych mamy do dyspozycji wspaniałą bibliotekę - libiconv oraz oprogramowanie je wykorzystujące. Biblioteka ta potrafi przekodowywać teksty pomiędzy wieloma różnymi kodowaniami i jest bardzo przydatna w normalnej pracy na przykład z plikami HTML.

Iconv jako program linii poleceń


Całkiem prawdopodobne, że w twoim systemie jest już obecny iconv. Sprawdź to:krzysztof@EP09:~$ iconv --version
iconv (GNU libc) 2.4
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Ulrich Drepper.


Jeśli jej nie masz, polecam doinstalować. Iconv potrafi przekodować plik oraz standardowe wejście (stdin). W przypadku wywołania programu bez parametru z nazwą pliku, iconv będzie oczekiwał na tekst w stdin. Argumenty programu są bardzo proste: -f kodowanie określa kodowanie pierwotne (from), a -t kodowanie określa kodowanie docelowe (to). Jako ostatni argument podajemy nazwę pliku. I tak, w przypadku kodowania z ISO do Unikodu wywołamy program następująco:
krzysztof@EP09:~$ iconv -f ISO-8859-2 -t UTF-8 zażółć.txt
zażółć gęślą jaźń
ZAŻÓŁĆ GĘŚLĄ JAŹŃ


Proszę zauważyć, że przekodowana zawartość została wyświetlona na standardowym wyjściu (stdout). Zawartość oryginalnego pliku się nie zmieniła. Iconv sam nie modyfikuje plików. Dlatego musimy przekierować stdout do pliku, żeby uzyskać pożądany efekt w postaci poprawnie zakodowanego pliku.

 krzysztof@EP09:~$ iconv -f ISO-8859-2 -t UTF-8 zażółć.txt > zażółć-utf.txt

Dobrze, ale w przypadku gdy mamy wiele plików do przekodowania, ręczne wpisywanie komendy w linię poleceń będzie nużące. Co z tym zrobić? Iconv nie obsługuje konwersji wielu plików na raz, więc należy sobie pomóc językiem powłoki.
krzysztof@EP09:~$ for i in `ls *.txt`; do
> cp $i /tmp/
> iconv -f ISO-8859-2 -t UTF-8 /tmp/$i >$i
> done


Takim prostym skryptem przekodujemy wszystkie pliki z rozszerzeniem txt. Kopiujemy te pliki do katalogu /tmp/ ponieważ nie możemy czytać i zapisywać do tego samego pliku na raz.

Lista kodowań (i ich nazw) możliwych do wykorzystania jest dostępna po uruchomieniu programu z parametrem --list.

Iconv i PHP


Tak, PHP również dorobiło się wykorzystania biblioteki libiconv. W PHP 5 cała rodzina funkcji iconv jest częścią języka, a nie oddzielnym modułem. Dzięki tym funkcjom możemy przekodowywać ciągi tekstowe, generować nagłówki listów, a nawet konwertować całą treść strony.

Funkcja iconv()

Jest to funkcja, która konwertuje ciągi tekstowe. Jej wywołanie jest proste:

<?php
    $tekst = "zażółć gęślą jaźń";
    $tekstiso = iconv("UTF-8", "ISO-8859-2", $tekst);
?>


Pierwszym argumentem jest kodowanie wejściowe, drugim docelowe, a trzecim argumentem jest ciąg znakowy do przekodowania.

Konwersja z buforowaniem wyjścia

Załóżmy, że mamy stronę, która jest napisana w Unikodzie, ale musi do przeglądarki dotrzeć wysłana w ISO-8859-2. Oczywiście możemy przekodowywać każdy plik strony oddzielnie, ale jest to bez sensu, bo może się okazać, że niedługo będzie potrzebna wersja unikodowa tej strony. Z pomocą w takiej sytuacji przychodzi buforowanie wyjścia, czyli funkcja ob_start(). W rodzinie funkcji iconv mamy funkcję ob_iconv_handler(), która będzie służyła jako uchwyt dla funkcji ob_start. Jednak żeby wykorzystać tę funkcję musimy ustawić jeszcze jej parametry. Służy do tego funkcja iconv_set_encoding. Przykład:

<?php
    iconv_set_encoding("internal_encoding", "UTF-8");
    iconv_set_encoding("output_encodinf", "ISO-8859-2");
    ob_start("ob_iconv_handler");
    // Tutaj możemy umieścić resztę kodu skryptu i strony zakodowanego w Unikodzie.
    // Do przeglądarki trafi ISO-8859-2
?>



Na początku ustawiamy parametry: wewnętrzne kodowanie i kodowanie wyjściowe. Potem tylko uruchamiamy buforowanie wyjścia i po sprawie.

Ale uwaga! Problem pojawi się przy odczytywaniu danych z formularzy. Przeglądarka wyśle skryptowi dane w kodowaniu takim w jakim była zadeklarowana strona. Jeśli kodowanie wewnętrzne skryptu jest inne to pojawia się niezgodność. W przypadku gdy pobieramy tylko liczby nie będzie kłopotów. Kłopoty pojawią się gdy będziemy obrabiać tekst. Dlatego należy o tym pamiętać i zabezpieczyć się na wszelki wypadek konwersją danych przesłanych z formularzy do wewnętrznego kodowania.

Generacja nagłówków e-mali


Dość ciekawą funkcją jest możliwość wygenerowania poprawnego nagłówka e-maila (tylko ze znaków występujących w ASCII) na podstawie ośmiobitowego lub unikodowego tekstu zarówno korzystając z metody Base-64 jak i Quoted-Printable. Służy do tego funkcja iconv_mime_encode(). Odwrotne działanie ma funkcja iconv_mime_decode(). Żeby zakodować potrzebna będzie tablica ustawień dla funkcji. Jest to tablica asocjacyjna z kilkoma kluczami:


scheme
    określa metodę kodowania: B dla Base-64 i Q dla Quoted-Printable


input-charset
    określa kodowanie ciągu znaków do przetworzenia


output-charset
    określa kodowanie do którego zostanie przekonwertowany ciąg przed przetworzeniem na nagłówek


line-length

    określa maksymalną długość linii nagłówka


line-break-chars

    określa w jaki sposób będą rozdzielane nowe linie w nagłówku, domyślnie jest to \r\n, ale równie dobrze może być to także \n


Dobrze, teraz przykład:
<?php
    $tekst = "zażółć gęślą jaźń";
    $ustawienia = array(
        "scheme" => 'B',
        "input-charset" => 'UTF-8');

    $naglowek = iconv_mime_encode("Subject", $tekst, $ustawienia);
?>



Nie ustawiłem tutaj wszystkich parametrów. Pozostawiłem je domyślne. Szczególnie ważny jest tutaj fakt nie ustawienia kodowania wyjściowego, dzięki czemu tekst nie był przekodowywany. Taki nagłówek możemy wykorzystać w funkcji mail().

 

Źródło: http://www.antylameriada.net/iconv/