Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

Cholerne ą-ę – czyli kodowanie w UTF-8

Studia jak to studia, wymuszają na biednym studencie zgłębianie dziwacznych zakamarków wiedzy. Tym razem moje ćwiczenia z Teorii Informacji i Kodowania poruszyły temat Unicode, a konkretniej UTF-8.

Skoro na co dzień używamy tego kodowania w wielu plikach, to warto go poznać. Sposoby, które przedstawię na łamach tego wpisu mogą okazać się szczególnie przydatne studentom. Nie mam zamiaru tłumaczyć tutaj dlaczego takie kodowanie jest fajne (lub nie) ani tony zbędnej teorii, a raczej praktycznie pokazać jak przed kolokwium albo sprawdzianem szybko nauczyć się kodować dowolne znaki w UTF-8.

UWAGA: Zakładam, że czytelnik w biegły sposób potrafi poruszać się pomiędzy systemem binarnym i heksadecymalnym.

Rys teoretyczny

Lata temu grupa mądrych ludzi zza oceanu wymyśliła, że znaki w dokumentach możemy kodować na podstawie tabeli ASCII. O ile ASCII tylko i wyłącznie w czystej postaci jest już dzisiaj rzadko spotykane, to w kodowaniu UTF-8 ma olbrzymie znaczenie – zaraz zobaczymy dlaczego.

Tabela ta oczywiście nie zawiera znaków innych niż z alfabetu łacińskiego, ponieważ amerykanie nie mieli takiej potrzeby. Próbowano to obejść przy pomocy stron kodowych. Ten kto jeszcze pamięta Windows 9x, ten wie o co chodzi ;)

Wracając to problemu kodowania znaczków innych niż w tablicy ASCII wymyślono tak:

  • Jeżeli znak, który chcemy zakodować znajduje się w tablicy ASCII, to zapisujemy go standardowo według jego kodu z tablicy
  • W przeciwnym razie posłużmy się sposobem kodowania, który wykładowca tłumaczył jak najbardziej zawiłym sposobem na wykładzie

Co to dla nas oznacza?

Jeżeli mamy literkę jak A lub e lub dowolną inną z tablicy ASCII, to wyszukujemy jej kod i wpisujemy w plik. Tak robią edytory tekstu przy kodowaniu UTF-8. Więc A zapiszemy jako 41h, e zapiszemy jako 101h.

Kodujemy!

Aby zakodować coś spoza kodu ASCII, użyjemy do tego kolejnej tabelki.

Dla każdego znaku z tabelki posłużymy się następującym algorytmem:

  • Odnajdujemy interesujący nas znak w tablicy i zapisujemy jego numer Unicode (U+x)
  • Rozpisujemy numer binarnie (uwaga, numer jest zapisany heksadecymalnie)
  • Kodujemy znaki w kolejnych „przedziałach” bitów grupowanych po 8, numerując każdy przedział kolejno bitami 10, 110, 1110, 11110...

Brzmi strasznie? Tylko w teorii. Zakodujmy dla przykładu literkę Ę.
1. Odnajduję kod literki Ę i zapisuję go: U+118.
2. Rozpisuję binarnie 118h: 000100011000, po pominięciu nic nie zmieniających bitów otrzymuję 100011000.
3. Koduję:

  • Próbuję zapisać w pierwszym przedziale bity. Numer pierwszego przedziału to 10. Mam do wykorzystania 6 bitów, więc biorę ostatnie 6 bitów naszego rozpisanego 118h: 011000. Po włączeniu numeru przedziału na początek otrzymuję: 1001 1000
  • Pozostały mi bity 00100. Koduję więc drugi przedział i numeruję go kolejno: 110. Łączę numer z pozostałymi bitami i otrzymuję: 1100 0100
  • Zakodowałem całą liczbę! Teraz łączymy przedziały: 1100 0100 1001 1000
  • W przeliczeniu na system szesnastkowy otrzymujemy C498h.

Jeżeli znamy podstawowe przeliczenia między systemami liczbowymi, to kodowanie w UTF-8 nie powinno sprawić nikomu problemu. A jak sprawdzić czy na pewno dobrze zadokowaliśmy znak?

Sprawdzenie

Z przyzwyczajenia używam dość archaicznego programu HexView

W ulubionym edytorze (w moim przypadku TextPad) zapisuję naszą przykładową literkę Ę i zapisuję, stosując kodowanie UTF-8.

Następnie w HexView otwieram nasz plik tekstowy i sprawdzam rezultat:

Jak widać, literka Ę jest zakodowana poprawnie :)

Podsumowanie

Jak widać nie taki diabeł straszny i spokojnie można nauczyć się kodowania znaków, a potem może i zaimplementować własny algorytm ich zapisu w swoje programy? Niezależnie od tego czy mój wpis służył w celu zaspokojenia własnej ciekawości, czy też nauki do kolosa na kierunku technicznym, mam nadzieję że pomogłem :) Jeżeli popełniłem gdzieś po drodze błąd, to proszę abyście mnie poprawili ;) 

internet porady inne

Komentarze

0 nowych
Shaki81 MODERATOR BLOGA  38 #2 13.03.2013 18:47

Czyli widać, że jak się chcę można sobie życia nie kompilować:)

Savpether   6 #3 14.03.2013 08:10

Skoro ma to być dla studentów, dodałbym, że UTF-8 jest taki pogmatwany, ponieważ programiści chcieli dać możliwość zapisania jak największej liczby znaków przy zachowaniu jak najmniejszego rozmiaru pliku.

Dlatego też, znaki ASCII z przedziału od 0 do 127 (kod podstawowy) jest kodowany na jednym bajcie. Jeden bajt składa się z 8 bitów, co daje nam możliwość zakodowania 2^8=256 znaków. Mamy 256 miejsc na kody przystankowe, znaki narodowe, znaki łacińskie, itd. Oczywiście to nie wystarczy do zakodowania wszystkich języków świata, np. Cyrylicy, czy Chińskiego, dlatego wymyślono, że jeśli w UTF-8 coś nie da się zakodować na jednym bajcie, to pierwszy bajt będzie wskazywać na ilość bajtów następujących po nim, ten bajt zawiera wartości z przedziału C0 do FD(szesnastkowo). Następnie następuje sekwencja zadanych bajtów z wartościami z przedziału 80 do BF.

Takie kodowanie pozwala nam zakodować plik tekstowy skrojony na miarę. Jeśli będzie zawierał tylko znaki podstawowe ASCII to będzie ważył tyle ile ASCII, jeśli inne to jego waga wzrośnie, ale nie od razu podwójnie (nie przeznaczy od razu na każdy znak po 2 bajty zamiast jednego, tylko na niektóre).

Dlatego też Microsoft powinien przestawić wszędzie w swoich edytorach domyślnie z kodowania Windows 1250 na UTF-8. Windows 1250 wywodzi się jeszcze z czasów Latin2, jest starsze od UTF-8. Dlaczego tego pragnę? Bo jeśli korzystam z Polskiego Windowsa 8 załóżmy i napiszę tekst - załóżmy, że w notatniku i wyślę go do Polaka, np. w Rosji, a on odczyta go na swoim laptopie z Rosyjskim Windowsem 8 to zobaczy krzaki. Jakbym zamiast Windows 1250 wybrał UTF-8 to on zobaczyłby normalne znaki. Z tym, że załóżmy, że jestem normalnym, zwykłym użytkownikiem. Jak myślicie, będzie mnie obchodzić zmiana kodowania?

Właśnie z tego powodu użytkownicy GNU/Linuksa muszą w swoich odtwarzaczach filmów przerzucać kodowanie z nowoczesnego UTF-8 na archaiczny Windows 1250, bo napisy powstają w większości na Windowsie w notatniku, a on domyślnie zapisuje w Windows 1250 i u Linuksowców wychodzą krzaki przy znakach narodowych, bo znaki narodowe w Windows 1250 wykorzystują kody rozszerzone ASCII z przedziału od 128 do 255, których UTF-8 nie wykorzystuje :)

Dlatego właśnie domyślnie powinien być UTF-8.

Fajny artykuł, planowałem jakiś czas temu napisać coś o konwersji z ASCII na UTF-8 i na odwrót w Windowsowym assemblerze MASM i chyba mnie zainspirowałeś :D

Autor edytował komentarz.
  #4 17.03.2013 16:46

W razie czego tutaj jest wszystko co trzeba, w miarę łatwo opisane:
http://www.crimsteam.site90.net/crimsteam/html/html_dodatki_zestawyznakow_wstep....

Razi   5 #5 17.03.2013 17:33

UTF-8 powinien być stosowany wszędzie, może i znaki narodowe zajmują 2 bajty, ale nie ma żadnych ograniczeń - dostępne są wszystkie znaki, na polskim forum można wkleić (o zgrozo) tekst po niemiecku, czy nawet rosyjsku i nie ma krzaczków.

Też mnie wkurza przerzucanie kodowania w VLC na cp1250, albo jeszcze gorzej: iso-8852-2, bo nigdy nie wiadomo w czym ktoś to zakodował (a jak zauważyłem windowsowe edytory zapisują w tych 2 kodowaniach na przemian, choć częściej cp1250).

Pablo_Wawa   9 #6 17.03.2013 20:58

Od siebie jeszcze dodam, że sam Unicode reprezentuje znaki z wielu krajów, najbardziej popularnym (głównie z Europy) zostały przypisane kody od 0 do 65535, dzięki czemu niektóre kodowania (np. UCS-2) operują na 2 bajtach na znak (używa się tego np. w SMSach, korzysta z tego również Microsoft w swoich programach - Wordzie czy Excelu).

Algorytm kodowania znaku jako UTF-8 jest prosty - zakładając, że kod znaku jest w zmiennej input, mamy:
http://imageshack.us/photo/my-images/600/utf8.jpg
Wrzuciłem link do obrazka, bo blog wycinał kod.
Jak można się domyślać, operacje te najwygodniej jest zakodować w asemblerze.

Warto dodać, że znak w UTF-8 może zajmować kilka bajtów (nawet 4-5) - np. dla kodów znaków powyżej 0x200000 (2097152 dziesiętnie) zajmie już 5 bajtów.

Fakt, że obecnie mamy małe zamieszanie z kodowaniem CP1250, ISO-8852-2 (Latin 2) i UTF-8, ale to jeszcze nic w porówaniu do chaosu w latach 80. i 90., kiedy było tych "standardów" kodowania kilkanaście u użyciu.

Autor edytował komentarz.
Saskatchewan   8 #7 20.03.2013 16:14

Mały błąd w tekście: "e", to 0x65, a nie 0x101.