(toiminnot)

hwechtla-tl: Pythonin unicode-tuki

Kierre.png

Mikä on WikiWiki?
nettipäiväkirja
koko wiki (etsi)
viime muutokset


Huom! Tämä on nillitys. Ja tässä mainitut asiat on korjattu Python3:ssa: http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit

Olen vähän niin kuin tottunut ajattelemaan, että kun Unicode-tuki otetaan johonkin kieleen, se tehdään niin, että merkkijonot ovat siitä lähtien sisäisesti Unicodea ja niitä muunnetaan tavuvektoreista ja tavuvektoreiksi silloin, kun ollaan tekemisissä ulkoisen maailman kanssa. Tämä tarkoittaa, että tiedostoihin, verkkoyhteyksiin ja näytölle kirjoitettaessa tai niistä luettaessa määritetään, miten kyseisen virran tavut tulkitaan unicodeksi. Kielen sisällä ei tarvitse olla tekemisissä minkään muunnosten kanssa.

Eipä vain Pythonissa. Python ottaa sen sijaan käyttöön kaksi merkkijonotyyppiä, "perinteisen" ja Unicoden. Unicode-merkkijonot ovat jotakuinkin ongelmattomia, paitsi että kun ne ovat tekemisissä perinteisten merkkijonojen kanssa, perinteiset muunnetaan Unicodeksi automaagisesti muunnoksella, joka käyttää systeemin "oletuskoodausta". Oletuskoodaus on oletuksena ASCII, mutta silti perinteisiin merkkijonoihin voi tunkea merkkejä, jotka eivät ole ASCIIta. Mitä ihmettä? Niinpä perinteisissä merkkijonoissa voi olla vaikka mitä tavuja, ja kaikki toimii, kunnes ne joutuvat tekemisiin Unicode-merkkijonojen kanssa, jolloin ne yritetään tulkita Unicodeksi ASCII-koodauksella. Perinteiset merkkijonot eivät kanna mukanaan tietoa omasta koodauksestaan: niissä on "jotain tavuja vain".

Perinteisistä merkkijonoista pystyy tekemään Unicode-merkkijonoja niiden decode-metodilla ja Unicode-merkkijonoista perinteisiä encode-metodilla; molemmille määritetään, mitä koodausta perinteinen merkkijono käyttää. Jostain syystä myös Unicode-merkkijonoilla on decode-metodi, josta en pysty kuvittelemaan, mitä se tekee. Sen perusteella, millaisia virheilmoituksia siitä tulee, se yrittää ilmeisesti muuntaa ensin Unicode-merkkijonon perinteiseksi oletuskoodauksella ja sitten tulkita sen takaisin Unicodeksi annetulla koodauksella. WTF?

Unicode-merkkijonoille on muodostin unicode, jolle voi antaa koodauksen, jota tulkintaan käytetään. unicode(merkkijono, "utf-8") tarkoittaa ilmeisesti suunnilleen samaa kuin merkkijono.decode("utf-8"). Tämä vaikuttaa kätevältä tavalta muuntaa kaikki unicodeksi, paitsi että Python ei näemmä osaa tehdä unicodesta unicodea, vaan tuottaa virheilmoituksen. Pitääkö tehdä lisää wrappereita, kun ei kielestä oletuksena löydy järkevää toimintaa?

Kaikki kirjastokutsut olettavat "perinteisten" merkkijonojen käytön. Jos haluan tiedoston, jonne voi kirjoittaa Unicodea, ei saa sanoa open("tiedosto", "w") vaan pitää sanoa import codecs; codecs.open("tiedosto", encoding="utf-8", mode="w"). Vaikuttaako kätevältä? Puhumattakaan siitä, että tästä ei ole Pythonin tutoriaaleissa mitään mainintoja. Pitää osata löytää codecs-moduulin sisällöstä.

Ylipäänsä koko juttu on tolkuttoman huonosti dokumentoitu. Pythonin sisäinen malli (kaksi merkkijonotyyppiä, joista toisesta (Unicode) tiedetään, mitä merkkejä siinä on, ja toisesta taas (perinteinen), mitä tavuja siinä on) on niin sekava, ettei melkein missään ole jaksettu selittää sitä kunnolla. Siellä, missä asiaa on selitetty, menee kielen sisäisten muunnosten selittämiseen sen verran voimia, ettei yleensä jakseta enää puhua siitä, kuinka ulkomaailman kanssa sitten ollaan tekemisissä. Ylipäänsä dokumentaatio asiasta on vähäistä ja netistä löytyvät tiedot vanhoja. Paras tietolähde, jonka löysin, oli tämä: http://evanjones.ca/python-utf8.html

Ehkä ärsyttävintä on se, että Python tekee hankalaksi hypätä kokonaan Unicode-merkkijonojen käyttöön. Merkkijonoja joutuu usein kirjoittamaan lähdekoodiin, ja Unicode-merkkijonojen syntaksi u"jotain" on ruma ja unohtuu helposti. Kaipa siihen tottuu, tietysti, mutta kyllästyttää ratkaista ongelmia, jotka johtuvat siitä, että jossain on taas unohtanut u:n merkkijonon edestä ja kun perinteinen merkkijono törmää muuhun maailmaan, se aiheuttaa koodausvirheitä.

Suurin osa erilaisista kirjastoista on kirjoitettu sillä oletuksella, että käytetään perinteisiä merkkijonoja. Sisäänrakennettu open() on vain yksi esimerkki. Merkkijonoja joutuu siis joskus muuntamaan kirjastojen käyttöä varten edestakaisin. Useimmista kirjastoista puuttuu myös dokumentaatio siitä, millaisia oletuksia ne tekevät käytetyistä merkkijonoista. Samoin Pythonin peruspalveluista.

Jos on ihan pakko olla kaksi merkkijonotyyppiä, voisi edes tehdä siitä Unicodesta (joka oikeasti sisältää merkkejä eikä tavuja) oletuksen ja muuntaa kaikki aiemmat merkkijonoihin liittyvät palvelut Unicode-merkkijonojen palveluiksi. Sitten voisi lisätä tavuvektorin niitä käyttöjä varten, mihin sitä tarvitaan. Muutoksessa käyttäjän rajapinnan erona on käsittääkseni lähinnä se, että Unicode-merkkijonoista voi löytää merkkejä, joiden ordinaali on suurempi kuin 255. Ehkä tämä sitten rikkoisi kaiken?

kategoria: ohjelmointi


kommentoi (viimeksi muutettu 29.09.2010 15:40)