HTML-entiteiten decoderen

Plak tekst waarin de tekens door hun ontsnappingsvormen zijn vervangen en lees terug wat er staat. Namen als `&`, decimale verwijzingen als `&` en hexadecimale als `&` worden allemaal herkend. Het gebeurt met een tabel en niet door de tekst als HTML te renderen, want dat laatste zou vreemde markup in deze pagina uitvoeren.

Resultaat

Het antwoord verschijnt hier terwijl je typt.

  • Waar het draait

    Er wordt niets geüpload, want er is geen bestand — het wordt in deze pagina uitgerekend.

  • Geen wachtrij, geen account

    Het antwoordt zo snel als je machine kan, en vraagt nooit wie je bent.

  • Zo vaak als je wilt

    Er wordt niets geteld en niets begrensd — nog eens antwoorden kost ons niets.

Hoe het werkt

  1. Plak de tekst met de tekenverwijzingen erin.
  2. Lees het resultaat. Wat niet herkend wordt of onvolledig is, blijft staan in plaats van stil te verdwijnen.
  3. Kopieer wat je nodig hebt. Er is niets geüpload.

Met een tabel, en niet met een parser

De voor de hand liggende aanpak is de tekst in een onzichtbaar element zetten en de browser het werk laten doen. Dat is ook de gevaarlijke: op dat moment wordt vreemde markup binnen deze pagina verwerkt, en een `img`-tag met een adres erin veroorzaakt meteen een netwerkverzoek.

Daarom staat er hier een tabel met de namen en een routine voor de numerieke vormen, en wordt er niets gerenderd. Het kost een lijst en het levert een pagina op die geen verzoek uitlokt met tekst die iemand anders heeft geschreven — precies de eigenschap waar de rest van deze sectie ook op leunt.

Drie vormen die allemaal voorkomen

Een teken kan als naam staan, als decimaal getal of als hexadecimaal getal: `&`, `&` en `&` zijn alle drie de ampersand. Welke vorm je aantreft hangt af van wat het heeft geschreven, en in één document komen ze vaak door elkaar voor.

De numerieke vormen zijn de betrouwbaarste, want ze werken zonder tabel: het getal is het Unicode-codepunt. De namen zijn leesbaarder en er zijn er meer dan tweeduizend, waarvan de meeste nooit iemand tegenkomt. Deze pagina neemt ze alle drie aan, want kiezen is niet aan de lezer.

De puntkomma die ontbreekt

HTML staat voor een korte, vaste lijst oude namen toe dat de puntkomma ontbreekt: `&amp`, `&lt` en `&copy` worden nog steeds herkend. Voor alle andere namen geldt dat niet, en dan blijft de tekst gewoon staan zoals hij is.

Daar zit een berucht randgeval: `&notit;` wordt gelezen als `¬it;`, want `&not` staat op de oude lijst. Een tekst waarin een ampersand zonder puntkomma voor een woord staat, kan dus onbedoeld iets anders worden — de reden dat een ampersand in gewone tekst altijd als `&` hoort te worden geschreven.

Waarom je soms twee keer moet decoderen

Levert het resultaat nog steeds `<` op, dan was de invoer twee keer ontsnapt: er stond `<`, en de eerste ronde maakte daar `<` van. Dat is geen fout in het decoderen maar de juiste uitkomst van één stap.

Twee keer decoderen tot het leesbaar is, is verleidelijk en verkeerd als het om code gaat. In een keten die maar één keer ontsnapt, sloopt die reparatie de tekst. Het is een aanwijzing om te zoeken waar er twee keer wordt ontsnapt, en dat is bijna altijd een applicatie plus een sjabloon die het allebei doen.

Waar dubbele entiteiten vandaan komen

De gebruikelijke bron is een tekstveld waarvan de inhoud al ontsnapt in de database staat en daarna nog eens door de sjabloonmotor gaat. Het tweede geval is een export uit een CMS dat zijn eigen ontsnapping meelevert, en het derde is een RSS-feed waarin HTML in een veld is gestopt dat zelf HTML is.

De feed is het interessantste geval, want daar is het geen fout: de inhoud van een `description` in RSS is per definitie ontsnapte HTML, en de dubbele laag hoort erbij. Wie zo’n veld verwerkt, moet dus precies één keer decoderen en het resultaat als markup behandelen, niet als tekst.

Ruim tweeduizend namen, en de lijst is gesloten

De HTML-standaard kent ruim tweeduizend benoemde tekenverwijzingen, van `&` tot namen voor wiskundige symbolen die vrijwel nooit iemand gebruikt. Die lijst is bevroren: er komen er geen bij, ook niet voor nieuwe Unicode-tekens.

Voor alles wat er niet in staat — en dat is verreweg het meeste van Unicode, emoji inbegrepen — bestaan alleen de numerieke vormen. Dat is geen tekortkoming: een naam voor elk codepunt zou een tabel van honderdduizenden regels betekenen, terwijl het getal het codepunt al is.

Numerieke verwijzingen voorbij het basisvlak

Tekens boven `` — emoji, zeldzame schrifttekens, sommige wiskundige symbolen — hebben een codepunt dat niet in zestien bits past. `😀` is de lachende emoji, en JavaScript bewaart die intern als twee halve stukken, een surrogaatpaar.

Dat is waar zelfgebouwde decoders stuklopen: wie het getal naar één teken omzet met een functie die maar zestien bits aankan, krijgt onzin. Hier wordt het codepunt als geheel omgezet, zodat emoji en zeldzame tekens er als één teken uitkomen en niet als twee losse helften.

De spatie die je niet ziet

` ` is geen gewone spatie maar U+00A0, de harde spatie, die in UTF-8 de twee bytes `c2 a0` is. Op het scherm ziet hij eruit als een spatie en voor elke tekstvergelijking is hij een ander teken.

Daar komen de raadselachtige verschillen vandaan bij tekst die uit een tekstverwerker of van een webpagina is gekopieerd. Een zoekopdracht vindt niets, een sleutel komt niet overeen, een validatie faalt op een veld dat er leeg uitziet. Wie deze pagina gebruikt en een spatie ziet waar hij geen ` ` verwachtte, heeft de oorzaak te pakken.

Wat er met ongeldige verwijzingen gebeurt

Een naam die niet bestaat, een getal boven het hoogste codepunt of een verwijzing zonder afsluiting blijft hier staan zoals hij is. Dat is met opzet: verwijderen zou tekst weggooien, en gokken zou een teken opleveren dat er nooit stond.

Het is ook de nuttigste uitkomst bij het opsporen van een fout. Een `&` die is blijven staan, is het bewijs dat er een losse ampersand in de bron stond die niet als `&` was geschreven — en dat is een echt probleem in het document en niet in het decoderen ervan.

Gedecodeerde tekst is geen veilige HTML

Wat hier uitkomt is de oorspronkelijke tekst, en die kan alles bevatten wat de schrijver erin heeft gezet, tags inbegrepen. Het decoderen maakt de tekst leesbaar; het maakt hem niet ongevaarlijk om ergens in te zetten.

Belandt de uitkomst weer op een pagina, dan hoort er opnieuw te worden ontsnapt, en moet het als markup worden gebruikt, dan hoort er een sanitizer overheen die tags en attributen tegen een lijst legt. De volgorde is: decoderen om te lezen, en per bestemming opnieuw beslissen wat ermee mag.

In XML kent bijna geen enkele naam

XML heeft er maar vijf ingebouwd: `<`, `>`, `&`, `"` en `'`. Alle andere namen, ` ` voorop, bestaan daar niet tenzij ze in een document type definition zijn verklaard — en een XML-parser die er een tegenkomt, stopt met een fout in plaats van hem over te slaan.

Dat is de reden dat een RSS-feed of een SVG-bestand kan struikelen over tekst die in HTML probleemloos werkt. Kom je dat tegen, dan is de oplossing de naam vervangen door zijn numerieke vorm: ` ` in plaats van ` ` werkt in allebei de werelden zonder verklaring vooraf.

AVG bij tekst uit een vreemde bron

De tekst die hier binnenkomt komt vaak uit een databaseveld, uit een supportsysteem of uit een export, en draagt dan namen, adressen en berichten van klanten. Omdat het decoderen op de pagina gebeurt, wordt daar niets van aan ons verstrekt.

Er is nog een tweede reden dat het lokaal blijft, en die is technisch: er wordt niets gerenderd, dus vreemde markup kan hier ook geen verzoek uitlokken dat de inhoud naar een derde stuurt. Dat is geen bijvangst maar de reden dat er met een tabel wordt gewerkt en niet met een parser.

HTML-entiteiten decoderen: veelgestelde vragen

Waarom staat er nog `<` in mijn resultaat?

Omdat de invoer twee keer ontsnapt was: er stond `&lt;`, en één ronde decoderen maakt daar `<` van. Dat is het juiste resultaat van één stap; de oorzaak is een keten die twee keer ontsnapt.

Waarom wordt `&amp` zonder puntkomma wel opgelost?

Omdat HTML voor een korte, vaste lijst oude namen toestaat dat de puntkomma ontbreekt. Voor alle andere namen niet. Daar komt ook het randgeval vandaan waarin `¬it;` als `¬it;` wordt gelezen.

Kan vreemde markup hier iets uithalen?

Nee. Er wordt niets gerenderd: het decoderen gebeurt met een tabel en een routine voor de getallen. Een img-tag in je invoer blijft tekst en veroorzaakt geen enkel verzoek.

Wat is het onzichtbare teken dat uit `&nbsp;` komt?

De harde spatie, U+00A0, in UTF-8 de bytes c2 a0. Hij ziet eruit als een spatie en is voor elke vergelijking een ander teken — de gebruikelijke oorzaak van zoekopdrachten die niets vinden.

Verlaat de tekst mijn machine?

Nee. Het decoderen gebeurt op deze pagina. Dat telt, want deze tekst komt meestal uit een databaseveld, een supportsysteem of een export met gegevens van klanten erin.

Andere hulpmiddelen