Unicode

De ce pula mea ai vrea sa poti scrie nume de fisiere in telugu? Ok, stramosii tai n-au fost in stare sa realizeze ca 20 si ceva de semne sint suficiente pentru a reprezenta pe foaie sunetele care le ies pe gura, dar s-au prins altii intre timp. Cum cacat sa insisti sa scrii cu viermisori si iatagane dupa ce ai vazut alfabetul latin? Si, mai rau, cum sa-ti vina ideea sa aduci haosul asta in software, in loc sa le faci un bine inapoiatilor si sa le spui ca daca vor tehnica de virf pentru vazut porn, trebuie sa se invete si cu tehnica de virf pentru scris si citit? الجهاد!

Faptul ca lumea civilizata i-a bagat in seama p-astia a fost prima greseala. A doua greseala a fost formarea unui Comitet responsabil cu recensamintul alfabetului si incartiruirea mazgaliturilor intr-un Standard International. Acest Comitet ar fi putut sa adune literele latine, chirilice, kana si eventual grecesti (cu perversiunile lor gen ăâöé etc.), sa le puna intr-o tabela si sa termine treaba (ignorind ostentativ ideogramele si jihadistii), dar nu asa functioneaza comitetele. Cind ti se confera puterea de a Gindi O Solutie, trebuie sa faci ceva complicat, ca sa le arati celorlalti ca esti destept. Orice prost poate sa puna citeva sute de caractere intr-o tabela, dar tu esti un erudit patrunzator si vezi imaginea de ansamblu, asa ca tabela ta va contine hieroglifele egiptene, caracterele ugaritice, cartile de joc, emoticoane, piesele de domino si mahjong etc. Si privind tu asa la cele un milion de semne adunate, simti ca tot nu-i destul si tot se vor gasi diversi sa conteste valoarea ta, valoarea ta, asa ca adaugi si scrijeliturile de pe discul din Phaistos (caracterele 0x101D0 – 0x101FF), care nu a fost descifrat inca. CINE-I CEL MAI ERUDIT ACUM?

Si nu te opresti aici. Nefiind prost si tinind cu tarie sa arati lumii asta, te apuci sa incurajezi creativitatea in exprimare prin intermediul semnelor diacritice. Desi ai bagat in tabela toate formele de A cu cerculet, accente, sedila, puncte si combinatii, faci caractere separate pentru toate aceste semne, ca sa poata omul sa scrie A cu trema fie ca Ä, fie ca A urmat de ¨.

Astfel s-a nascut Unicode, un standard menit sa ingreuneze pe cit posibil reprezentarea digitala a textului. Unicode se asigura ca orice operatiune de bun simt pe care ai vrea s-o faci pe un string devine imposibila. De exemplu, daca ai avut ghinionul sa inveti ca un string e o insiruire de caractere, imperialistul dornic de globalizare din tine va avea pretentia ca al 5-lea caracter dintr-un string sa fie la pozitia 5. In termeni stiintifici, vei dori ca accesul la caractere sa fie in timp O(1). Ei bine, Unicode se pisa pe pretentia ta din multiple directii.

In primul rind, cea mai folosita encodare pentru Unicode este UTF-8, care e un cod cu lungime variabila. Asta s-a intimplat din cauza ca, desi sint vreo 1.1 milioane de caractere in Unicode, oamenii normali folosesc cam 100 din ele, deci s-a simtit nevoia unei reprezentari compacte. Astfel, un caracter poate avea intre 1 si 4 bytes, deci textu’ e mic, dar ca sa ajungi la un caracter trebuie ori sa treci prin toate caracterele de dinainte, adica O(N), ori sa tii si sa actualizezi o tabela de index impreuna cu string-ul, care ar fi mai mare decit spatiul cistigat prin encodarea cu lungime variabila.

Minerii se vor grabi sa observe ca Microsoft au rezolvat demult problema asta prin intermediul string-urilor wide, unde un caracter e tinut pe 16 biti. Minerii sint prosti si nu realizeaza faptul ca 1.1 milioane de caractere nu incap in 16 biti. String-urile wide din Windows folosesc de fapt UTF-16, care este tot un cod cu lungime variabila si are aceleasi probleme ca UTF-8, plus ca ocupa de doua ori mai mult spatiu pentru textul de oameni normali, trebuie convertit la UTF-8 pentru a discuta cu restul lumii si e endian-dependent, asa ca inainte sa te exprimi pe limba lui trebuie sa semnalezi ce endianness preferi cu un cacat numit byte order mark.

Sigur, 1.1 milioane de caractere pot fi reprezentate pe 32 de biti. Se pare ca problemele noastre se rezolva daca folosim UTF-32 si acceptam ca 75% din text sa fie 0, ca memoria si discurile sint ieftine in zilele noastre. Ei bine, gratie diacriticelor separate (sau “combining marks” cum se numesc ele oficial) se rezolva o pula. Daca pentru codul tau “un caracter” inseamna “o litera” si nu “o litera sau un semn de cacat care face parte din litera anterioara”, tot trebuie sa parcurgi string-ul ca sa ajungi la o anumita pozitie si ca bonus trebuie sa si stii care sint semnele alea ca sa tii cont de ele cind numeri. Evident, celelalte reprezentari au si ele problema asta.

Sa facem un efort de imaginatie: un miner-arhitect este insarcinat sa toarne fundatia unui cod ce va folosi UTF-8. Sesizind problema indexarii, dinsul infaptuieste o functie numita CharacterAt() care parcurge string-ul in cautarea pozitiei date, tinind cont de lungime variabila, diactritice separate si alte mui. In urma sa trudeste minerul-programator ce este insarcinat sa caute slash-urile dintr-un string. Miinile lui negre iti vor intinde o floare asemanatoare cu codul ce urmeaza:

for(size_t i = 0; i < Length(str); ++i)
{
    if(CharacterAt(str, i) == '/')
    {
        // ceva
    }
}

Am incercat aici sa cuprind cit mai bine mentalitatea de miner, folosind size_t pentru variabila i, ca asa a auzit ca-i bine, dar chemind Length() la fiecare iteratie, deoarece minerii nu inteleg ce face de fapt functia aia. Oricum, chiar daca minerul muta apelul catre Length() in afara buclei, cautarea asta e tot O(N^2). Cam ca atoi-ul Minerului Suprem, mai tineti minte?

Apropo, Length() ala trebuie sa aplice aceleasi principii de convietuire globala ca CharacterAt() ca sa afle cite caractere sint de fapt in sir, nu merge sa cauti primul 0.

Exista diversi mintosi care sustin ca accesul aleatoriu la caractere este necesar foarte rar. De obicei cauti ceva printr-un string, deci oricum iei la rind toate caracterele, asa ca ai nevoie doar de niste facilitati de iteratie care sa stie de regulile pulii. Chiar daca le dam dreptate, ramine comparatia, un alt detaliu prin care Unicode da muie programatorilor din lumea intreaga.

Un om sanatos s-ar astepta sa poata compara doua string-uri byte cu byte: cind gasesti valori diferite la aceeasi pozitie, string-urile difera. Asta ar fi prea simplu, asa ca din nou diacriticele separate salveaza situatia. Ca sa compari doua string-uri trebuie intii sa le normalizezi, o operatiune cit se poate de simpla si naturala cu o descriere concisa, de doar 27 de pagini. Pentru a stimula si mai tare creativitatea, exista de fapt 4 moduri de normalizare, numite intuitiv D, C, KD si KC. Pe linga disclaimer-ul ala despre OOP ce insoteste codul Java din descriere, supun amuzamentului dumneavoastra si metoda recomandata de utilizare a implementarii din WINAPI: deoarece nu poti sti cit de mare o sa fie string-ul normalizat, chemi functia aia intr-o bucla pina cind o nimereste.

Dupa ce reusesti sa normalizezi string-ul ala trebuie sa incepi cu intrebarile filosofice: este sau nu string-ul ’10’ echivalent cu ‘Ⅹ’, ‘Δ’ sau ‘١٠’? Apropo, Ⅹ ala nu e X, este 0x2169, “Roman Numeral Ten” si in UTF-8 se scrie pe 3 bytes: 0xE2 0x85 0xA9. Ce ne faceam daca nu aveam in Unicode numerele romane de la 1 la 12? UNDE MAI ERA ERUDITIA NOASTRA?!

Deci muie Unicode, muie alfabet!

Tags: , , , , , , , , , , ,

20 Responses to “Unicode”

  1. Baluba Says:

    Ce te plangi atata? Comparatia e foarte usoara. Randezi textu pe ecran si compari pixel cu pixel. Alternativ, faci un RLE si ai semnatura unica. Poti sa zici ca nu e semnatura unica? :)

  2. Unu' Says:

    Ce nu vede mata aicea e că dacă tot nu foloseşti decât caracterele tale (din ASCII) atunci Stringul tău chiar dacă e în UTF-8 arată la fel ca în ASCII cu doar un byte/caracter.

  3. Tomis Says:

    Cel mai placut este cand vrei sa scrii cod cross-platform si nu folosesti un toolkit gen Qt ca sa nu te gandesti la Unicode. Aviz amatorilor:

    http://stackoverflow.com/a/402918

  4. Silviu Says:

    Tocmai ai descoperit unicodul si capul te doare? Nu-i nimic. Stai linistit ca trece.
    Pana sa intelegi tu ca pentru a prinde loc in piete mai traditionale si nationaliste, ca cele asiatice, mai trece ceva apa pe Dambovita. Altfel n-ai debita ineptii “cum sa-ti vina ideea sa aduci haosul asta in software, in loc sa le faci un bine inapoiatilor si sa le spui ca daca vor tehnica de virf”.
    Cunoscand fixismul din tine, nu ma mira.

    In vremuri in care unii nu se mai obosesc sa realoce memorie pentru stringuri ci pur si simplu creeaza noi obiecte in urma concatenarilor de stringuri (Java / C# unde schimba referinta la noul obiect creeat in urma concatenarii), tu te astepti ca cineva sa-ti dea access O(1) la un anumit caracter chinezesc? Dream on.
    E un lucru care m-a contrariat… dar am zis ca n-are sens sa-mi bat capul.
    Daca tu ai o problema, in loc s-o tratezi, o expui aici. Misto. ;)

  5. jos8cal Says:

    Bine, adevarul e ca nu toti au bataia lampasului reglata ca Silviu. La fel cum sub pamint schimbi tigari pe piine cu alti ingineri ca tine, tot asa iti faci si blog in “engleza” sa prinzi pe pietele celor slabi in limba.

    Cit priveste Obositii de Curte Veche care nu mai realoca, apeciez in mod deosebit paranteza cu C#. E ca la curve. Cind C++ te da afara din casa, te duci la curve si le povestesti amicilor pe bloguri diferite deal-uri in C#.

    Cit priveste batutul capului, pe viitor pune casca pe el, dupa care loveste. Daca veneai sa-ti iei cadoul de la noi vedeai ca are o carpa care protejeaza capul impotriva degajamentelor.

  6. Mihnea Says:

    Unu’: nu prea pricep ce ai vrut sa-mi comunici. Chiar crezi ca stiu despre combining marks si moduri de normalizare, dar nu stiu ca UTF-8 a fost gindit sa mearga ca un superset al ASCII? Cum adica daca folosesc ASCII? Ce, scriu softuri pentru mine? Daca codul merge cu ASCII, merge cu ASCII, nu cu “un pic de UTF-8”. Daca trebuie sa mearga cu UTF-8, trebuie sa tii cont de toate muile legate de normalizare, nu merge sa te prefaci ca nu exista. Daca nu tii cont, o sa vina un bou si o sa-ti ceara o resursa care se cheama pula\u0306.png si tu o sa-i zici ca n-ai, pentru ca la tine se cheama pulă.png.

    Silviu: nu te mai stradui sa ne arati ca esti prost, ca stim deja. Apreciez ca tocmai ai auzit de string-uri imutabile si simteai nevoia sa te lauzi chiar daca nu are nici o legatura cu discutia, dar te-am mai rugat sa intelegi ca noi, astia care ridem de tine, am invatat bazele programarii cu mult timp in urma, nu le deslusim (prost) acum. Si nu inteleg cum ai tupeul sa vorbesti despre complexitate aici, avind in vedere ca de la noi ai auzit prima data de conceptul asta. Tu o sa ai voie sa folosesti notatia aia cu O abia dupa ce ne explici cum ai fost in stare sa implementezi atoi() in timp patratic dupa 10 ani de experienta in C++, sau cit mai pretinzi acum ca ai.

    PS: da-mi voie sa repet o intrebare pe care ti-am mai adresat-o, dar nu stiu din ce motive ai omis sa raspunzi: exista abonati standupprogramming printre noii tai colegi?

  7. Stefan Says:

    Lol, Mihnea, există totuși o parte bună: mai puteai să fuți Unicode-ul în atâtea limbi fără ajutorul lui :)?

    Și că tot veni vorba de limbi, mi-a atras atenția “ebut iunikoda”, care (din cunoștințele mele minime de rusă) înseamnă “fute Unicode”, un mesaj destul de aproape de cel al articolului tău. Google translate îmi dă dreptate: dacă pun “I fuck Unicode” iese “Ia ebu Unikoda” :D.

    Altfel, din ce zici tu, mă bucur că n-am avut de-a face cu această tehnologie.

  8. George Says:

    Daca nu ar fi Unicode, multi oameni de la Microsoft n-ar mai castiga o paine cinstita :) (http://blogs.msdn.com/b/michkap/) – citind ce scrie nenea ala imi dau seama ca fie imi dedic zece ani din viata subiectului asta, fie las pe altii sa-si consume nervii si ma multumesc cu clasa String din .NET..

    Cu dedicatie din partea
    ﯹ – ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM :-)

  9. Mihnea Says:

    Pe blogu’ ala scrie de ce poti sa faci doua fisiere numite pulă in acelasi director pe NTFS?

    PS: mersi pentru ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM, e noul meu caracter preferat.

  10. Unu' Says:

    Tu ziceai că oricum nu foloseşti decât caractere normale, inventate de latini, fără diacritice & şit; de asemenea ziceai că vrei un byte/caracter şi acces O(1). Ce vroiam eu să spun e că şi atunci când ai de-a face un String în UTF-8, poţi să ignori că e în UTF-8 şi să-l consideri în ASCII (adică un byte/caracter şi acces O(1)) dacă ai doar caractere latine de bază.
    De unde ai tu stringul ăla? Din parsarea unui HTML sau un fişier sau mai ştiu eu de unde; nu tu trebuie neaparat să-l fi creat – tu doar îl procesezi.

    Dacă ai şi alte caractere atunci vin-o cu o soluţie mai elegantă să le înglobezi.

  11. Mihnea Says:

    Nu, eu ziceam ca mi-as dori sa se foloseasca doar caractere latine. Cum asta nu s-a putut, mi-as fi dorit sa folosesc caractere dintr-o lista de citeva sute sau mii, nu citeva milioane, ca sa incapa pe 16 biti. Nici asta nu s-a putut, asa ca mi-as fi dorit macar sa pot scrie un caracter intr-un singur fel. Din pacate, nici asta nu era suficient de complicat. Cam asta e esenta articolului.

    Am string-ul ala de la un utilizator. El scrie cu tastatura lui de erudit pedant ca vrea sa deschida pulă.png. Eu trebuie sa purced a cauta fisierul in cauza, care s-ar putea sa se cheme pula\u0306.png. Fisierul ala n-a fost facut de mine, deci nu merge sa normalizez string-ul si sa il gasesc, pentru ca poate aplicatia care l-a facut nu face normalizare. Sau poate face normalizare, dar in alt mod decit mine. De exemplu eu fac composition, el face decomposition. Nu merge nici macar sa incerc ambele variante, compuse si nu, pentru ca daca aplicatia care a facut fisierul nu face deloc normalizare, un utilizator creativ poate folosi un nume cu 5 caractere speciale, fiecare cu 3 moduri de scriere, si sa nu fie consistent. Poate scrie “q cu punct sus si jos” ca “q cu punct sus” urmat de “punct jos combining mark”, dupa care scrie “s cu punct sus si jos” ca “s” urmat de “punct sus” urmat de “punct jos”. Degeaba normalizez eu string-ul meu, daca numele fisierului pe disc e random. CreateFileW()/open()/fopen()/etc. nu-l vor gasi. Trebuie ori sa incerc toate variantele, ceea ce e incet si imbecil, ori sa listez toate fisierele din director, sa normalizez ce-mi vine si sa compar cu string-ul meu normalizat. Ajung sa fiu minerul care-si construieste singur cerul, respectiv functia open().

    Intelegi?

    Solutia eleganta ar fi fost sa nu existe combining marks, pentru ca avantajele pe care le aduc sint nesemnificative comparativ cu dezavantajele.

  12. mihai Says:

    simpatizez

  13. troll Says:

    Asa ca un mic news, ca oricum se posteaza foarte rar pe aici:)

    http://www.mobzine.ro/ionut-balan/2012/11/wowzapp-2012-sold-out-hackhatonul-de-la-bucuresti/?utm_source=twitterfeed&utm_medium=twitter

    Have fun :).

  14. troll Says:

    Ca sa completez, am gasit si un job despre existenta caruia nu stiam pana acum:

    http://www.linkedin.com/in/georgepristavu

    Platform Evangelism Lead

    Amin.

  15. jos8cal Says:

    Mie imi place asta: http://ro.linkedin.com/in/oanaterteleac?trk=pub-pbmap

    Large Opportunity Manager at Microsoft Romania

    Large Opportunity is Large.

  16. Mihnea Says:

    We need a war.

  17. Tomis Says:

    http://evernote.com/careers/job.php?job=o3qPWfwY

    Product Genius

    … what

  18. Kanghu Says:

    Nu mai apare nimic nou ? :(

  19. Catalin++ Says:

    Muia asta de unicode e asa de destept incat un cod, “U+4E0E” are caractere diferite pe Chineza(generica), Simplificata, Traditionala, Japoneza si Koreana. Difera o caciulita, un punctisor o liniuta. Numai un orezar ar putea sa vada din prima diferenta.
    Eu trimit p***uli in chineza la un capat si ala primeste serpisori in japoneza. Daca eu as vrea sa am un textbox care vrea sa suporte ambele limbi odata, cand imi vine caracterul p***ulii nu o sa stiu ce sa fac cu el, ca nu stiu in ce limba e! Muie unicode!

  20. Alex B Says:

    Heh, de la http://what-if.xkcd.com/34/ -> https://dev.twitter.com/docs/counting-characters

Leave a Reply

Optionally add an image (JPEG only)