Kirjautuminen

Haku

Tehtävät

Oppaat: PHP-ohjelmointi: Osa 17 - Merkistöt

  1. Osa 1 - Johdanto
  2. Osa 2 - Muuttujat
  3. Osa 3 - if-rakenne
  4. Osa 4 - for-silmukka
  5. Osa 5 - Taulukot
  6. Osa 6 - Lomakkeet
  7. Osa 7 - Nettisivusto
  8. Osa 8 - Lisää silmukoista
  9. Osa 9 - Tiedostot
  10. Osa 10 - Omat funktiot
  11. Osa 11 - Istunnot
  12. Osa 12 - Tietokannat
  13. Osa 13 - Tietoturva
  14. Osa 14 - Olio-ohjelmointi
  15. Osa 15 - Kuvien luonti
  16. Osa 16 - Säännölliset lausekkeet
  17. Osa 17 - Merkistöt
  18. Osa 18 - PHP:n ongelmat

Kirjoittaja: Antti Laaksonen (2011).

Tekstin merkistökoodaus määrittää, mitä merkkejä tekstissä voi käyttää ja miten merkit on koodattu lukuarvoiksi. Yleisimmät nettisivujen merkistökoodaukset ovat ISO-8859-1 ja UTF-8, joissa molemmissa on omat etunsa ja haittansa.

ISO-8859-1 sisältää 256 merkkiä, ja jokainen merkki vie tilaa yhden tavun eli 8 bittiä. Merkkien käsitteleminen on helppoa, mutta ongelmaksi voi tulla, että merkistöstä puuttuu tarvittavia merkkejä kuten euromerkki €. Ratkaisun ongelmaan tuo laaja Unicode-merkistö, jonka tavallinen koodaustapa on UTF-8. Siinä merkin koko on merkistä riippuen 1–4 tavua.

PHP-kielen suunnittelun lähtökohtana on ollut ISO-8859-1, minkä vuoksi UTF-8:n käyttäminen on hankalampaa. Keskeinen ongelma on, että PHP:n tavalliset merkkijonofunktiot olettavat merkkijonon jokaisen merkin olevan yhden tavun kokoinen. Tästä seuraa ongelmia, jos merkkijono on UTF-8-muodossa eikä oletus pidä paikkaansa.

Merkkijonon merkit

PHP:n funktiot ord ja chr muuttavat merkin merkkikoodiksi ja päinvastoin. Esimerkiksi merkin "A" koodi on 65, joten ord("A") on 65 ja chr(65) on "A".

Seuraava koodi tulostaa merkkijonon "esimerkki" merkkien koodit:

<?php
$merkkijono = "esimerkki";
for ($i = 0; $i < strlen($merkkijono); $i++) {
    $merkki = $merkkijono[$i];
    $koodi = ord($merkki);
    echo "{$merkki} = {$koodi} <br>";
}
?>

Koodin tulostus on seuraava:

e = 101
s = 115
i = 105
m = 109
e = 101
r = 114
k = 107
k = 107
i = 105

ISO-8859-1 vs. UTF-8

ISO-8859-1:ssä ja UTF-8:ssa koodeja 0–127 vastaavat merkit ovat samat. Näihin merkkeihin kuuluvat englannin kielen aakkoset a–z ja A–Z, numeromerkit 0–9 ja tavallisimmat välimerkit kuten pilkku ja huutomerkki. Niin kauan kuin merkkijonossa on vain näitä merkkejä, merkistöjen kanssa ei tule mitään ongelmia.

Suomalaiselle käyttäjälle merkistöjen erot näkyvät useimmiten siinä, että ääkköset koodataan niissä eri tavalla. Tarkastellaan esimerkiksi merkkijonoa "tähtiyö". ISO-8859-1:ssä merkkijono koodataan seuraavasti:

116228104116105121246
tähtiyö

UTF-8:ssa koodaus taas on seuraava:

116195164104116105121195182
tähtiyö

Koodausten ainoa ero on merkeissä ä ja ö. ISO-8859-1:ssä merkkien koodit ovat 228 ja 246. UTF-8:ssa taas merkkien koodaus vaatii kaksi tavua: merkin ä koodaavat tavut 195 ja 164 ja merkin ö koodaavat tavut 195 ja 182.

UTF-8:ssa jokaista merkkiä vastaa 1–4 tavun pituinen koodi. Periaatteessa mitä harvinaisempi merkki, sitä pidempi merkin koodi on. Lisäksi merkkien koodit on valittu niin, että minkään merkin koodi ei ole toisen merkin koodin osana. Esimerkiksi missään 2–4 tavun koodissa ei ole tavua väliltä 0–127.

PHP:n funktiot

PHP:n tavalliset merkkijonofunktiot olettavat, että jokaisen merkin koko on yksi tavu. Tarkastellaan seuraavaa koodia:

<?php
$merkkijono = "tähtiyö";
echo "Pituus: " . strlen($merkkijono);
?>

Jos tiedoston koodauksena on ISO-8859-1, koodin tulostus on odotusten mukainen:

Pituus: 7

Mutta jos koodauksena on UTF-8, koodin tulostus onkin seuraava:

Pituus: 9

Ongelmana on, että funktio strlen palauttaa suoraan merkkijonossa olevien tavujen määrän. UTF-8-koodauksessa kuitenkin merkit ä ja ö vievät kaksi tavua tilaa, mikä vääristää merkkijonon pituutta.

Äkkiseltään näyttää, että PHP:n koko merkkijonokirjasto on käyttökelvoton UTF-8-koodauksen yhteydessä. Tilanne ei ole kuitenkaan aivan näin huono, koska jotkin funktiot käsittelevät oikein myös UTF-8-muodossa olevat merkkijonot, vaikka niitä ei ole suunniteltu siihen. Esimerkiksi funktio substr_count toimii oikein, koska se vain vertaa merkkijonojen osia toisiinsa välittämättä koodauksesta.

mbstring-kirjasto

PHP:n mbstring-kirjasto (mb = multibyte) on tarkoitettu sellaisten merkkijonojen käsittelyyn, joissa merkki voi viedä tilaa useita tavuja. Kirjastoon kuuluvat funktiot tunnistaa mb-etuliitteessä. Kaikille PHP:n tavallisille merkkijonofunktioille ei ole kuitenkaan korviketta mb-kirjastossa, vaikka ne eivät käsittelisi oikein UTF-8-merkkijonoja.

Seuraava koodi laskee merkkijonon pituuden funktiolla mb_strlen:

<?php
mb_internal_encoding("UTF-8");
$merkkijono = "tähtiyö";
echo "Pituus: " . mb_strlen($merkkijono);
?>

Koodin tulostus on seuraava:

Pituus: 7

Aluksi koodi asettaa merkistökoodaukseksi UTF-8:n, koska mbstring-kirjasto tuntee monia muitakin koodauksia. Tämän jälkeen funktiolla mb_strlen voi laskea luotettavasti minkä tahansa UTF-8-merkkijonon pituuden.

Koodauksen valinta

Nettisivuston toteutuksessa on tärkeää, että merkistökoodaus on valittu kaikkialla johdonmukaisesti samaksi. Käytännössä:

  1. Kaikkien tiedostojen koodaus on oikea. Tiedoston koodauksen pystyy valitsemaan useimmissa tekstieditoreissa tiedoston tallennuksen yhteydessä. UTF-8-enkoodausta käytettäessä pitää huolehtia, että tiedoston alussa ei ole BOM-merkkiä (byte order mark).

  2. Palvelin ilmoittaa nettiselaimelle oikean koodauksen. Tämän voi tarvittaessa varmistaa PHP:n header-funktiolla seuraavasti tiedoston alussa:

    header("Content-Type: text/html; charset=X");

    Tässä kohtaan X tulee ISO-8859-1 tai UTF-8 riippuen koodauksesta.

  3. HTML-koodin head-osiossa ilmoitettu koodaus on oikea:

    <meta charset="X" />

    Tässä kohtaan X tulee ISO-8859-1 tai UTF-8 riippuen koodauksesta.

  4. Jos käytössä on tietokanta, myös sen asetuksissa koodaus on oikea. MySQL-tietokantayhteyden merkistön voi asettaa esimerkiksi seuraavalla kyselyllä heti yhdistämisen jälkeen:

    SET NAMES X

    Tässä kohtaan X tulee latin1 tai utf8 riippuen koodauksesta.

Tällä hetkellä ISO-8859-1 on usein oletusarvoinen merkistökoodaus, minkä vuoksi UTF-8:n käyttäminen vaatii enemmän vaivaa. Toisaalta UTF-8 on selvästi nykyaikaisempi valinta ja helpottaa erikoismerkkien ja vieraiden kielten käyttöä sivustolla.


Kirjoita kommentti

Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.

Muista lukea kirjoitusohjeet.
Tietoa sivustosta