Codepunt, code-eenheid, byte
Drie verschillende tellingen. Voor het grootste deel van de tekst op de wereld verschillen ze, en de meeste bugs hier komen doordat mensen ze als één ding behandelen.
- Een codepunt is het nummer dat Unicode aan een teken geeft. De raketemoji is U+1F680, 128.640 decimaal. Codepunten lopen tot U+10FFFF.
- Een code-eenheid is het stuk van vaste grootte waar een codering uit is opgebouwd. Een JavaScript-string is UTF-16, dus elk codepunt boven U+FFFF wordt als twee opgeslagen, een surrogaatpaar. Eén raketemoji heeft lengte 2, en
charCodeAt(0)geeft 55.357. - Een byte is wat een bestand of socket vervoert. UTF-8 gebruikt één byte tot U+007F, twee tot U+07FF, drie tot U+FFFF en vier daarboven. De raket is één codepunt, twee UTF-16 code-eenheden, vier UTF-8 bytes:
f0 9f 9a 80.
Waarom de voor de hand liggende oplossing fout is
Door de tekens lopen en van elk charCodeAt nemen klopt voor ASCII en voor niets anders, want dat geeft UTF-16 code-eenheden in plaats van bytes. Voer er een emoji in en je krijgt d83d de80, twee getallen groter dan een byte, allebei een surrogaathelft. Plak dat in een decoder en er komt niets terug. Deze tool converteert via TextEncoder en TextDecoder, zonder aan te nemen hoeveel bytes een teken kost.
Bytes op een willekeurige plek afknippen is dezelfde fout een laag lager. UTF-8 afkappen om in een kolom van 255 bytes te passen kan midden in een teken landen en een leidende byte zonder vervolg achterlaten, en daar komt U+FFFD vandaan.
Veelvoorkomende problemen
- Een oneven aantal hexcijfers betekent dat er bij het kopiëren een is weggevallen. Dat wordt gemeld en niet aangevuld, want elke gok verandert je data.
- Een groep groter dan 255 betekent decimaal zonder scheiding, of het verkeerde grondtal.
72 105als hex lezen vraagt om een byte van 261. - Geen geldige UTF-8, met een losse byte tussen 0x80 en 0xFF, betekent dat de bytes uit Latin-1 of Windows-1252 komen: een e met accent aigu is daar 0xE9 en in UTF-8 0xC3 0xA9. Een
0xEF 0xBB 0xBFvooraan is een byte order mark, onzichtbaar in de gedecodeerde tekst en een veelvoorkomende reden dat twee identiek ogende bestanden niet gelijk zijn. - Uitvoer van een andere converter met d83d of dc00 erin is die tool die UTF-16 code-eenheden uitspuugt en ze bytes noemt. 0xD800 tot 0xDFFF zijn surrogaten, nooit bytes, en tekst die zo is omgezet overleeft de weg terug niet.
Veelgestelde vragen
Is dit ASCII of UTF-8?
UTF-8, en dat bevat ASCII: de eerste 128 codepunten zijn byte voor byte identiek.
Kan ik binair terug omzetten naar tekst?
Ja, in elk grondtal, en de invoer mag rommelig zijn: spaties, komma's en regeleinden scheiden allemaal groepen, en voorvoegsels worden weggehaald. Binair en hex hebben een vaste breedte, dus de groepering wordt weggegooid en de cijfers worden vanaf links opnieuw opgedeeld, waardoor 0100 1000 en 01001000 dezelfde invoer zijn. Decimaal en octaal kunnen dat niet, want een 12 145 zonder opvulling is niet 12145: met scheidingstekens is elke groep één byte, en zonder worden de cijfers alleen in drieën gedeeld als het aantal door drie deelbaar is, en anders als één getal gelezen.
Waarin verschilt dit van Base64?
Base64 propt drie bytes in vier printbare tekens om ze te vervoeren: compact, niet leesbaar. Een grondtal schrijft elke byte als een eigen groep, van één decimaal cijfer tot acht binaire, met elke byte leesbaar. Gebruik de Base64-encoder om data te verplaatsen, bestanden inbegrepen, want die neemt deze tool niet aan.
Waarom levert één emoji vier bytes op?
Omdat UTF-8 de meeste emoji in vier bytes codeert, en veel emoji daarbovenop uit meer dan één codepunt bestaan. Een vlag is twee regionale indicatoren, en een gezinsemoji is een aantal mensen aan elkaar geplakt met zero-width joiners. Het aantal bytes is het eerlijke antwoord; het aantal tekens hangt ervan af wat je een teken noemt.