Results 1 to 13 of 13

Thread: Unicode, een inleiding

  1. #1

    Unicode, een inleiding

    Unicode, een inleiding

    Inleiding (op de inleiding)
    Ik wil hierbij een beginnetje maken met wat uitleg over unicode. Er worden op NLDelphi veel vragen gesteld die meestal te herleiden zijn tot het gebruik van Delphi 2009 en het onbegrip van de werking van de Unicode strings.
    Nu heb ik zelf nog niet veel gedaan in deze nieuwe versies van Delphi, dus ik weet eerlijk gezegd niet precies wat voor functionaliteiten er zijn om strings om te zetten van de ene encoding naar de ander. Ik weet ook niet wat er voor andere pitfalls zijn m.b.t. unicode strings. Ik schat echter in dat het allemaal wel meevalt, en dat je al een heel eind bent als je weet wat het verschil is tussen UTF-16 en UTF-8. Ik denk dat ik het zelf weet, maar mocht je er een fout in ontdekken, spreek me er dan gerust op aan.

    Onderstaande tekst is een uittreksel uit eigen kennis en diverse Wikipedia-pagina's. Het is geen compleet overzicht van alles dat met unicode te maken heeft, maar het bevat denk ik voldoende (en misschien al wel te veel) om je duidelijk te maken waarom je file in Delphi 2009 ineens een hoop spaties bevat, of als je Delphi 2009 string in een Delphi 7 messagebox maar 1 teken lijkt te bevatten.

    Wil je toch meer weten, begin dan maar te lezen op http://en.wikipedia.org/wiki/Unicode.

    Wat is unicode?

    Unicode is een internationale standaard voor de codering van binaire codes naar grafische tekens en symbolen, vergelijkbaar met de ASCII-standaard. De standaard voorziet alle tekens en symbolen (in dit artikel "karakters" of "tekens" genoemd) van alle geschreven talen van een nummer.

    ASCII-standaard? Inderdaad, laten we bij het begin beginnen.

    Het begin

    ASCII
    Toen vroeg?óh alles beter was, en de wereld nog veel kleiner dan nu, vond iemand een computer uit. Voor een computer is alles getalletjes, dus ook karakters zijn voor zo'n computer gewoon een getal. In eerste instantie werden er 7 bits voor uitgetrokken, zodat er 128 verschillende karakters konden worden gemaakt. 95 daarvan zijn leesbare westerse tekens, namelijk hoofd- en kleine letters, cijfers en leestekens. De 33 overgebleven tekens zijn speciale stuurtekens. Hiermee kon je de primitieve computer vertellen wat er precies moest gebeuren. Zo'n stuurteken kan bijvoorbeeld een linefeed, carriage return of tab teken zijn, maar ook een geluidje, backspace, en allerlei scheidingstekens horen bij deze stuurtekens. Diverse van deze tekens refereren naar handelingen op een 'analoge' typmachine. Hierop kun je met een grote hendel je vel papier een regel opschuiven (linefeed), en het koetswerk (carriage) laten terugschuiven, zodat je aan het begin van die schone regel begint (carriage return).

    Die stuurtekens hebben in de huidige Windows omgeving hun nut grotendeels verloren. Op een wat lager niveau, bijvoorbeeld bij de aansturing van printers spelen ze nog wel een rol.

    Click image for larger version. 

Name:	800px-ASCII_Code_Chart-Quick_ref_card.jpg 
Views:	6367 
Size:	90.9 KB 
ID:	4940
    ASCII tabel

    Het vervolg

    ANSI
    Al snel kwamen mensen op het idee dat het wel handig zou zijn om meer tekens te kunnen maken, zeker toen men ook in niet-engelse landen met die apparaten wilde spelen. In eerste instantie was dat geen issue, want de A in ASCII staat tenslotte voor 'American'.

    Inmiddels was de 8-bit byte aardig ingeburgerd (dat is niet altijd zo geweest!), en werden dus ook de tekens 1 bit groter gemaakt. Een teken kan nu 256 verschillende waarden hebben, een hele verbetering.

    Dat is natuurlijk nog lang niet genoeg, want behalve tekens werden er ook symbolen toegevoegd om bijvoorbeeld lijnen te kunnen tekenen op die oude consoles. Daarbij heeft elke vreemde taal een flinke hoeveelheid extra tekens.

    Om daar omheen te werken, werd er gewerkt met code pages. Een code page geeft dus eigenlijk aan in welke taal je typt, of welke karaktersset je wilt gebruiken. Een gebruikelijke code page was 437. Deze karakterset bevatte letters met diacrieten, maar ook de bovengenoemde lijnen en wat griekse symbolen die veel gebruikt worden in wiskundige en natuurkundige formules. Hiermee kon zo'n beetje elk West-Europees land wel uit de voeten.

    Een overzicht van de karakters in deze code page vind je nog hier:

    Click image for larger version. 

Name:	OEM437.png 
Views:	7238 
Size:	30.6 KB 
ID:	4941
    Code page 437
    http://msdn.microsoft.com/nl-nl/gogl...8en-us%29.aspx


    NB: Veel DOS-software, maar ook Windows zelf, biedt hulp bij het maken van deze tekens. Wanneer je je toetsenbordinstellingen op Nederlands zet kun je gecombineerde tekens maken door bijvoorbeeld eerst een ' te tikken en daarna een e.

    NB: De échte programmeur ergert zich daar natuurlijk aan, omdat je dan altijd een spatie extra moet indrukken als je een kwootje in je code wilt. In plaats daarvan zet de échte programmeur z'n toetsenbordinstellingen op Amerikaans en leert hij de Alt-codes uit z'n hoofd. Onder DOS en Windows kun je karakters maken door Alt ingedrukt te houden en een code te tikken. Windows 'promoot' viercijferige codes, maar de oude driecijferige codes werken ook nog. Wanneer je Alt+130 intikt krijg je een é. Dat wil zeggen, als je de juiste code page hebt, want in een andere code page kan dezelfde waarde een andere betekenis hebben!

    De ASCII-sets met extra tekens worden 'extended ASCII' genoemd. Windows gebruikt echter de naam ANSI voor (een deel van) de code-pages van extended ASCII, daarom heb je van al die API calls een 'A' variant, en hebben we ook een AnsiString type in Delphi.

    Wat de naam ook is, het probleem blijft hetzelfde: je kunt maar 256 verschillende tekens tegelijk gebruiken. Je kunt dus moeilijk Nederlands en Grieks tegelijk schrijven, en met Chinees kom je helemaal in de knoei omdat die taal van zichzelf al meer tekens heeft dan de beschikbare 128.

    De oplossing (?)

    UCS
    UCS staat voor Universal Character Set. Dit is lijst van alle bekende tekens in alle hedendaagste en historische talen, alsmede muzieknoten en meer van zulks. Elk teken heeft een uniek nummer dat een code point wordt genoemd, en heeft een unieke, beschrijvende naam.
    In deze lijst is er dus geen twijfel wat elk teken betekent. Elk getal hoort bij één specifiek karakter. Er zijn dus geen code pages.

    Er is ruimte voor 1114109 tekens gereserveerd in deze lijst.

    De UCS is opgedeeld in planes. Elk plane bevat een groep van min of meer bij elkaar horende tekens. De eerste plane is de BMP, de Basic Multilingual Plane. Dit bereik, dat de code points 0 tot 65336 bevat ($0000 t/m $FFFF), bevat vrijwel alle gebruikelijke tekens in moderne talen. Een plane is weer onderverdeeld in scripts. De code points 0000 - 007F bevatten bijvoorbeeld het script 'Basic Latin', met daarin o.a. het westerse alfabet. Dit script komt niet geheel toevallig precies overeen met de ASCII tabel!

    Encodings
    Een encoding is een formule of omrekentabel om (een relevant deel van) de UCS compacter op te kunnen slaan.

    Op dit moment bevat de UCS al meer dan honderdduizend tekens, en dat kunnen er meer dan een miljoen worden. Dat betekent dat je al 3 bytes nodig hebt om elk teken op te slaan.
    Het is natuurlijk nogal inefficiënt om elk teken op te slaan in 3 of 4 bytes. Ook wil niet alle software alles opslaan in 3 bytes. Het neemt tenslotte een hoop extra ruimte in en het is niet compatible met software die niet deze volledige lijst met tekens ondersteunt, en alleen om kan gaan met de eerder genoemde ANSI lijst.

    Er zijn daarom verschillende encodings verzonnen. Dat zijn formules of tabellen om een code point (een teken in de UCS) om te rekenen naar een andere waarde. Op die manier kun je je beperken tot een deel van de UCS en heb je minder ruimte nodig om die tekens op te slaan. Eén van die encodings is bijvoorbeeld ASCII. Met ASCII kun je 128 specifieke tekens maken. Iemand die weet dat een stukje data in ASCII geschreven is, kan de afzonderlijke bytes van die data omzetten naar UCS code points. Op die manier weet je welk teken het is in de UCS en kun je het ook weer omrekenen naar een andere encoding, wanneer dat nodig is.

    UCS-2
    UCS-2 is een encoding die is ontworpen om een belangrijk deel van de UCS weer te geven. UCS-2 gebruikt 2 bytes om een teken op te slaan. Deze 2 bytes slaan rechtstreeks op de eerste 65536 tekens van de UCS. Dit is precies de BMP, de Basic Multilingual Plane. Door de encoding UCS-2 te gebruiken kun je dus alle moderne tekens tegelijk gebruiken in één character set. De grootte blijft schappelijk omdat een teken maar 2 bytes in beslag neemt, en geen 3 of 4.

    WideString
    De WideString zoals die in Delphi beschikbaar is, gebruikt UCS-2 als encoding. In deze WideString is elk teken dus 2 bytes. Je kunt er zo'n beetje elk gangbaar teken mee maken, maar helaas niet álle tekens uit de UCS.

    UTF-16
    UTF staat voor Unicode Transformation Format. Het is een Multibyte Character Encoding. Dat wil niet alleen zeggen dat elk teken meerdere bytes beslaat, maar ook dat dat een variabel aantal bytes is, afhankelijk van het teken! Niet elk teken is dus even groot, maar elk teken is wel minimaal 16 bits, ofwel 2 bytes groot, of een veelvoud daarvan. Een blokje van 16 bits wordt daarom de code unit van UTF-16 genoemd.

    UTF-16 is voor een groot deel compatible met UCS-2. Het verschil is dat een (ongebruikt) deel van de tekens van UCS-2 gebruikt worden om waarden mee samen te stellen. Twee van deze code units vormen samen een teken buiten de BMP. Ook deze encoding heeft dus wel een eindige hoeveelheid tekens, maar kan wel op een efficiënte manier alle tekens uit de UCS opslaan door de meestgebruikte tekens (die in de BMP) compacter op te slaan dan de minder gebruikte tekens.

    Windows 2000 en hoger gebruiken niet meer UCS-2, maar UTF-16 als standaard encoding. Delphi blijft tot versie 2009 nog een beetje achter.
    Let op: dit is een aanname. Ik weet niet helemaal zeker of Delphi de WideString ziet als UTF-16, UCS-2 of afhankelijk is van het platform. Het doet voor het verhaal ook niet zo erg ter zake, tenzij je Chinees of muziek wilt gaan schrijven.

    UTF-8
    UTF-8 werkt eigenlijk op een vergelijkbare manier als UTF-16. Een code unit in UTF-8 is 1 byte. Ook hier kunnen één of meer code units een code vormen die om te rekenen is naar een code point in de UCS. De eerste 128 tekens zijn 1 op 1 om te rekenen naar de eerste 128 tekens in de UCS. Dit is het Basic Latin script waar ik eerder naar verwees. Dit zijn dus tevens de 128 tekens uit de ASCII tabel.

    Hierdoor is UTF-8 forward compatible met ASCII. Tekst die in ASCII geschreven is hoeft dus niet geconverteerd te worden, maar kan gewoon behandeld worden alsof het UTF-8 tekst is! De hogere code units kunnen gecombineerd worden. 2, 3 of 4 code units vormen samen een teken door ze om te rekenen tot een code point in de UCS.
    Ik zal je de exacte formules besparen, maar ook UTF-8 is een efficiënte manier van opslaan. De veelgebruikte ASCII tekens beslaan maar 1 byte. De 1920 volgende tekens beslaan maar 2 bytes. Daarmee kun je Hebreeuws, Arabisch en diverse Cyrillische talen schrijven. Met de combinaties van 3 code units kun je de overige tekens uit de BMP maken, en met de combinaties van 4 bytes maak je de overige Unicode tekens.
    UTF8 is dus erg handig omdat het compact is, en forward compatible met ASCII. Het is daarom erg geschikt als encoding voor websites en e-mail. Daarbij is het theoretisch eenvoudig uit te breiden om nog meer tekens te kunnen bevatten, zonder dat het invloed heeft op eerdere teksten.

    UTF-32
    UTF-32 wordt niet veel gebruikt, maar het is wel de makkelijkste encoding. Elk teken beslaat 4 bytes. Hierdoor zijn alle code points in de UCS direct te gebruiken als waarde voor een code unit in UTF-32. Er is dus geen omrekening nodig. Het nadeel is dat tekst op deze manier wel erg veel ruimte inneemt. UTF-32 wordt daarom haast niet gebruikt.

    Byte Order Mark (BOM)
    Een probleem dat nogal eens wil optreden bij het lezen en schrijven van bestanden, is de endianess van numerieke waarden. De twee belangrijkste varianten zijn big endian en little endian. Bij het opslaan van een integere waarde in big endian, wordt de most significant byte vooraan geplaatst. Bij little endian is het net andersom. De waarde 1024 (hex 400) zou in big endian dus $0400 zijn en in little endian $0004. Deze volgorde is uiteraard van belang voor het juist interpreteren van tekens die uit meer dan één byte bestaan, zeker als deze bestanden uitgewisseld worden tussen verschillende systemen. Om dat op te lossen is er een teken gekozen dat maar op één manier geinterpreteerd kan worden. Het teken $FEFF bestaat wel, maar $FFFE bestaat niet. Als deze twee bytes aan het begin van een bestand staan, dan kun je dus zien of het bestand is opgeslagen in big endian (de eerste byte is $FE) of little endian (de eerste byte is $FF). Daardoor weet je ook hoe je de rest van de file moet interpreteren.

    In UTF-8 bestanden is het ook toegestaan om een BOM in het bestand te plaatsen, maar het wordt afgeraden. Het nut zou zijn, zodat je kunt zien dat het om een unicode bestand gaat, en niet om een ASCII/Ansi bestand. Dat is ook het enige nut, want UTF-8 heeft geen last van de endianness; de bytes staan altijd in dezelfde volgorde. Het wordt afgeraden omdat niet elke toepassing correct met dit teken omgaat. Een bekend voorbeeld is PHP. De drie bytes van de BOM worden door PHP niet herkend en daarom gewoon als output naar de browser gestuurd. Dit levert problemen op als je daarna nog headers wilt versturen. Het is daarom beter om UTF-8 op te slaan zonder BOM.

    Delphi en Unicode

    Delphi 3: WideString
    Delphi kent de WideString sinds versie 3. De WideString is geïntroduceerd om compatible te zijn met de BSTR ofwel OLEString van Windows. Deze string wordt gebruikt in COM. Dat is ook de reden dat deze string in Delphi 3 werd geïntroduceerd. Delphi 3 is namelijk de eerste versie die interfaces ondersteunt.
    De WideString is in tegenstelling tot de standaard (Ansi)string niet reference counted, maar daar hoef je je bij normaal gebruik geen zorgen om te maken.

    Delphi 2009: Unicode
    In Delphi 2009 is Unicode (verder) geïntroduceerd. Dit zorgt bij veel mensen nogal voor verwarring. Het standaard string type van Delphi is nu namelijk een alias voor een Unicode string met een UTF-16 encoding en niet meer voor de AnsiString waarin elk teken maar 1 byte groot is. String is nu dus eigenlijk (bijna) een alias voor WideString. Het grootste verschil is dat de unicode string van Delphi wél reference counted is, net zoals de AnsiString, maar daar zullen de meeste mensen geen boodschap aan hebben. Het is bij dagelijks gebruik dus wel handig om string gewoon als alias voor WideString te zien.

    Elk teken in deze string beslaat 2 (of soms meer) bytes. Tekens in de ASCII tekenset hebben dezelfde waarde als in UTF-16. Het enige verschil is dat ze nu als double byte teken opgeslagen worden. In plaats van #80 dus als #0080. Als je die tekst bekijkt in een editor die denkt dat het ANSI is (en dus ook in Delphi 7), lijkt tussen elk 'gewoon' teken een spatie te staan die bij nadere inspectie een #0 blijkt te zijn. Datzelfde 'probleem' was er trouwens ook al als je WideStrings gebruikte.

    Conversie
    Delphi biedt diverse functies om strings naar elkaar te converteren. Zo is het mogelijk om Unicode strings te vertalen naar Ansi strings in een bepaalde code page. Op die manier kun je je tekst bijvoorbeeld opslaan in code page 437, zodat een é weer de code #130 krijgt, en je string compatible is met DOS applicaties.

    UTF-8
    Als alternatief ondersteunt Delphi 2009 ook de UTF8String. Dit is ook een unicode string, maar dan met een UTF-8 encoding. Deze is dus niet compatible met de AnsiString! De ASCII tekens zullen hetzelfde zijn, maar een é zal in een UTF-8 string 2 bytes beslaan!

    Tot zover deze beknopte samenvatting. Ik hoop dat deze Nederlandstalig opsomming een wat betere leidraad is dan de verspreide informatie in de Engelse Wikipedia.
    Misschien dat wat concrete toepassingen het geheel nog wat meer zouden verduidelijken, maar daarvoor zou ik eerst de help-file eens moeten lezen. Dat laat ik dus graag over aan iemand anders.
    Last edited by GolezTrol; 01-Sep-10 at 18:11.
    1+1=b

  2. #2
    Wat betreft bestanden in windows zou je nog wat kunnen toevoegen over de BOM:

    http://en.wikipedia.org/wiki/Byte_order_mark
    Nederlandse Firebird Nieuwsgroep
    Hoe meer je weet, hoe meer je beseft hoe weinig je weet.

  3. #3
    Klein stukje toegevoegd. Bedankt voor de tip!
    1+1=b

  4. #4
    Pfft, wat zou het toch handig zijn als we gewoon allemaal dezelfde taal zouden spreken . Maar tot dat zover is: ik haal altijd door elkaar wat ook alweer waar zat en waarom en hoe... enzo.. dus dankjewel!
    Marcel

  5. #5

  6. #6
    Da's een mooi linkje, dat ik ook wel heb doorgelezen voor ik aan dit artikeltje begon. Volgens mij staat er ook weinig in dat ik niet verteld heb, en zo ja, dan heb ik het vast bewust weggelaten.
    1+1=b

  7. #7
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Ik dacht dat widestring UCS2 of UTF-16 is afhankelijk van Windows versie. Windows XP en Win 2000 sp3 + zijn dacht ik UTF-16?

    Overigens worden steeds meer toch wel handige symbolen toegevoegd buiten de BMP, dus helemaal kan je dat niet uitgummen.

    Verder zou ik het aantal tekens in de BMP noemen (in de 40000, zie unicode.org)

  8. #8

  9. #9
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Je kan ook eens naar dit oude FPC discussie document kijken:

    http://www.stack.nl/~marcov/unicode.pdf

    met name de "economics" paragraaf en de [] operator paragraaf.

  10. #10
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Quote Originally Posted by GolezTrol View Post
    UTF-32
    UTF-32 wordt niet veel gebruikt, maar het is wel de makkelijkste encoding. Elk teken beslaat 4 bytes. Hierdoor zijn alle code points in de UCS direct te gebruiken als waarde voor een code unit in UTF-32. Er is dus geen omrekening nodig. Het nadeel is dat tekst op deze manier wel erg veel ruimte inneemt. UTF-32 wordt daarom haast niet gebruikt.
    Teken <> codepoint. Een teken (character) kan uit meerdere codepoints bestaan.

    Het typische geval zijn talen (zoals b.v. Hebreeuws en ik geloof ook Arabisch) die geen klinkers vermelden en met accenten hints kunnen geven over de uitspraak (welke klinker). (als iemand meer weet over Semitische talen, corrigeer me aub als ik fout zit)

    Het aantal letter - accenten combinaties zijn zo hoog dat ze niet allemaal een eigen codepoint kunnen krijgen.

    Bij de Semitische talen zou dat misschien nog gaan, maar er was nog een andere taal (ik geloof Thai, in ieder geval ergens in die (Indochina) hoek) die dit compositie systeem nog veel erger had, en met meerder dimensies (meerdere accenten per karakter)

    Ze worden echter wel als een enkel teken op het scherm gerendered.

  11. #11
    Het is maar hoe je het bekijkt. Een oe of ui is ook niet één teken. Nou heb ik niet zoveel verstand van Hebreeuws, maar ik kan me voorstellen dat dat op een soortgelijke manier werkt. Code points zijn in ieder geval unieke symbolen. Misschien dat in sommige talen meerdere code points samengevoegd worden tot een enkel teken, maar ik dacht eigenlijk van niet.

    Op een scherm worden die tekens wel opgebouwd uit meerdere onderdelen. Iets dergelijks heb je hier trouwens ook. Door truukjes uit te halen met AltGr, of door simpelweg je invoertaal op Nederlands te zetten kun je een é maken door eerst een ' en daarna een e te tikken. Volgens mij werken computers voor die talen op een soortgelijke manier. Elk van die tekens heeft voor zover ik weet wel een uniek code point, wat ook de reden is dat er al meer dan een miljoen tekens in die lijst staan, waarvan nog geen 200 zijn ingeruimd voor westers schrift.

    Maar met een slag om de arm, ik ben er niet helemaal zeker van.
    1+1=b

  12. #12

    is een font een unicode-font?

    Hallo GolezTrol

    Ik wil een fontcombobx programmeren.
    Dat is op zich niet zo??n probleem.

    Enkel wil ik naast de fontnaam een glyph
    welke aangeeft of het om een ??normale?? TTF-Font
    of een UNICODE-Font gaat.

    Dus: het geinstallerde dont binnen Windows wordt
    'uitgelezen' naam van de font (en dan kan ik ook nog
    in de hint bijv 'AaBbCcDdDeEe' laten weergeven.
    Met naast de font glyphy voor Unicode of TTF-font.

    Heb je wellicht een paar regels code voor mij waarmee ik
    kan uitlezen of het om een unicode-font gaat of niet?

    Bedankt

  13. #13
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Quote Originally Posted by GolezTrol View Post
    Het is maar hoe je het bekijkt. Een oe of ui is ook niet één teken.
    Dat klopt, maar je combineert ze dan ook niet tot een teken als je ze "print". De combinatie is alleen verbaal.

    Nou heb ik niet zoveel verstand van Hebreeuws, maar ik kan me voorstellen dat dat op een soortgelijke manier werkt. Code points zijn in ieder geval unieke symbolen. Misschien dat in sommige talen meerdere code points samengevoegd worden tot een enkel teken, maar ik dacht eigenlijk van niet.
    Het schijnt dat er combinaties tussen unicode 4 en 5+ zijn toegevoegd, in ieder geval voor Hangul (een Koreaanse encoding iirc). Maar die liggen dan buiten de BMP.

    De vraag is of er nog een ander script is. Maar ik heb ff geen tijd om dat uit te zoeken. Wikipedia lijkt te zeggen dat het niet altijd zo is, maar noemt geen voorbeelden.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •