Unicode-normaliserare

Unicode har fyra normaliseringsformer, och de är inte utbytbara. NFC och NFD ordnar om samma tecken mellan en kodpunkt och en bas plus en kombinerande markering; NFKC och NFKD går längre och skriver om tecken som bara ser ut som varandra — ligaturen fi, fullbredds-A:et A, den inringade ①, den romerska siffran Ⅻ. Den här sidan kör samma text genom alla fyra, skriver ut en rad per tecken som anger vad som ändrades och varför, räknar grafemer mot kodpunkter och UTF-8-byte, och jämför två strängar under den form som väljs. Den andra halvan handlar om tecken som aldrig syns alls: nollbreddsmellanslag, sammanfogare, dubbelriktade styrtecken, variationsväljare, C0- och C1-kontrolltecken, teckenkodpunkter för privat bruk och trasiga surrogathalvor, var och en med sin position och sitt namn, samt ett alternativ att ta bort dem från en kopia av texten.
All bearbetning sker lokalt i webbläsaren — data laddas aldrig upp till någon server.

Osäker på vad normalisering är? Unicode-normalisering förklarad på enkel svenska — varför é kan vara ett tecken eller två, och varför det bryter en inloggning.
Form att rapportera och jämföra med:
Jämför de två rutorna efter:
NFC (inget än)
NFD (inget än)
NFKC (inget än)
NFKD (inget än)
Inget har jämförts än. Klistra in de två strängarna och tryck på Jämför de två strängarna.
första strängen, normaliserad (inget än)
andra strängen, normaliserad (inget än)
Inget har skannats än. Tryck på Sök efter osynliga och farliga tecken och sedan på Ta bort dem om man vill ha texten utan dessa tecken.
texten med de valda klasserna borttagna (inget än)
Ctrl+Enter i den första rutan kör det aktiva läget.
Välj ett läge och klicka på dess åtgärdsknapp. Normalisera skriver alla fyra former av texten och en rad per tecken; Jämför anger om två strängar är lika efter normalisering; Skanna listar tecknen som inte syns och kan ta bort dem. Självtestknappen kör om varje dokumenterat fall i sidans datafil, varje tecken som tabellerna för osynliga och kontrolltecken anger, och rapporterar antalet här.
Varje tecken, en rad vardera:vad tecknet är, vad de fyra formerna gör med det och varför. En skuggad rad är ett tecken som minst en form ändrar; NFD-kolumnen visar vad tecknet dekomponeras till, när det alls dekomponeras.
Hur många tecken är det?Samma text räknad på fyra sätt: grafemkluster (vad en läsare räknar), kodpunkter (vad Unicode tilldelar), UTF-16-enheter (vad JavaScript indexerar) och UTF-8-byte (vad som överförs). Normalisering flyttar talen mellan den andra och tredje kolumnen utan att ändra den första.
Det som hittades, i den ordning det förekommer:positionen räknar kodpunkter, klassen är den som tecknet tillhör, och noten säger vad tecknet gör med det som läser texten härnäst.
Klasserna som den här sidan behandlar som misstänkta:tabellen skrivs ut från skannern själv, så orden på sidan och knapparnas beteende kan inte glida isär. Allvarlighetsgraden handlar om effekten på en jämförelse, inte om hur sannolikt det är att tecknet är oskyldigt.
Tecken för privat bruk listas som misstänkta av en annan anledning än resten: de renderas, men de har ingen överenskommen betydelse, så samma kodpunkt är en annan bild i varje typsnitt som definierar den.
Hur stor indata sidan accepterar, och varför:normalisering är en ombyggnad av hela strängen, och den här sidan gör flera sådana plus en skanning och en grafemräkning, så kostnaden växer i rak linje med indatats längd. Gränserna nedan finns eftersom tabellen per tecken ritas i dokumentet, och en tabell med en miljon rader är precis vad som faktiskt får en webbläsare att stanna.
Vad är Unicode-normalisering?

Unicode ger fler än ett sätt att skriva en del tecken. Bokstaven é kan vara en enda kodpunkt, U+00E9, eller ett vanligt e följt av en kombinerande akut accent, U+0065 U+0301. De två ser likadana ut på skärmen och skrivs ut lika, men som strängar är de olika: de har olika längd, olika UTF-8-byte, och 'é' === 'e\u0301' är false.

Det är inte en kuriositet, det är en klass av fel. Vilken form man får beror på tangentbordslayouten, operativsystemet, inmatningsmetoden och om texten någonsin har kopierats via ett ordbehandlingsprogram. En lösenordskontroll, ett unikt index i databasen, en blockeringslista eller en cache-nyckel som jämför strängar byte för byte kommer att behandla de två stavningarna som två olika värden — så en användare kan ställa in ett lösenord på en enhet och inte kunna logga in från en annan, och samma ord kan förekomma två gånger i en lista över "redan upptagna" namn.

Versionen på 30 sekunder: NFC och NFD är samma text skriven på två sätt — NFC komponerar (e + accent blir ett tecken), NFD dekomponerar (ett tecken blir e + accent). De är avsedda att vara förlustfria och är de två former som ska användas för jämförelse och lagring. NFKC och NFKD gör allt det första två gör och ersätter sedan även kompatibilitetstecken: ligaturen fi blir två bokstäver, ½ blir 1⁄2, den romerska siffran ⅲ blir XII och ett fullbreddstecken A blir ett vanligt A. Det är precis vad ett sökindex eller ett nyckelordsfilter vill ha, och precis vad man aldrig ska tillämpa på ett lösenord.
Varför fyra former och inte en

Uppdelningen finns eftersom två olika mål tjänades. Att komponera och dekomponera text är en kanonisk ändring: resultatet betyder samma sak, och för de två första formerna går ingenting förlorat. Att vika ihop ligaturer, upphöjda tecken, inringade bokstäver och fullbreddsformer är en kompatibilitets-ändring: den görs för att underlätta matchning, och den förstör skillnaden mellan två strängar som Unicode säger är olika. De fyra formerna är alla kombinationer av de två besluten.

Tabellen ovan är fallistan från sidans datafil, kontrollerad mot plattformens egen String.prototype.normalize, och självtestet kontrollerar varje rad i den framför användarens ögon. Läs de två första raderna tillsammans: de är samma text, skriven på två sätt, och efter normalisering är de samma sträng.

Arbetade exempel

Det här är strängarna som exempelknappen går igenom, normaliserade live av sidan i stället för att skrivas in som svar. Varje sträng valdes för att en form gör något synligt med den.

Kompatibilitetstecken: vad NFKC kostar

NFKC och NFKD är förlustbringande, och förlusten är avsiktlig. De svarar på frågan "är det här samma ord", inte frågan "är det här samma sträng". Tre konsekvenser följer, och alla tre är lätta att få fel i produktionskod:

  • Lagra aldrig ett lösenord, en token eller en signatur i en kompatibilitetsform. Två olika indata skulle kollapsa till samma lagrade värde, vilket gör en längd- eller teckenkontroll svagare. Normalisera lösenord med NFC (eller inte alls) och jämför dem som byte efter att exakt samma sak gjorts på båda sidor.
  • En kompatibilitetsfold kan förvandla ett tecken till flera. Ligaturen fi är en kodpunkt som blir två bokstäver, så ett fält som validerades för längd före normalisering kan vara längre efteråt. Normalisera först, validera sedan och lagra.
  • NFKC tar inte bort allt osynligt. En variationsväljare överlever NFKC och NFKD, och så gör en nollbreddssammanfogare. Därför har den här sidan ett skanningsläge: normalisering svarar på en fråga om ett tecken form och identitet, och skannern svarar på frågan om något över huvud taget finns där.
Grafemer, kodpunkter och byte

Normalisering ändrar det andra av dessa tal och lämnar oftast det första orört. De tre talen betyder alla "textens längd" och de är alla olika, vilket är anledningen till att ett formulärfält med en gräns på 20 tecken kan tillhöra en sträng som är 20 kodpunkter, 30 UTF-16-enheter och 47 byte lång.

Talet i grafemkolumnen är vad en person räknar. Gapet mellan det och antalet kodpunkter är precis vad skannern handlar om: ett namn som består av 12 kodpunkter kan visas som 12 bokstäver, som 3 bokstäver med 9 osynliga markeringar mellan dem, eller som en enda emoji.

Tecknen som inte syns

Ett nollbreddsmellanslag tar ingen bredd, ändrar ingen glyf och är ett riktigt tecken med en riktig kodpunkt. Två identifierare som ser likadana ut på skärmen kan skilja sig åt med just ett sådant tecken, vilket är hur ett namn i en lista kan vara en bedragare som sorteras, söks och autentiseras annorlunda än namnet bredvid. Samma sak gäller för de dubbelriktade styrtecknen, som kan få en sträng att renderas i en annan ordning än den skrevs i — ett knep som har använts för att dölja filnamn.

Skannern i det tredje läget listar var och en av dem med sin position: nollbredds- och sammanfogartecken, dubbelriktade styrtecken, variationsväljare, ovanliga blanksteg, C0- och C1-kontrolltecken, teckenkodpunkter för privat bruk och en surrogat som inte är en halva av ett giltigt par, vilket inte är ett tecken alls. Klasstabellen längre upp på sidan förklarar vad varje klass gör, och knappen för borttagning tar bort dessa tecken från en kopia av texten utan att röra indata.

Misstagen som den här sidan är byggd för att fånga
  • Att jämföra två strängar utan att normalisera dem. 'é' === 'e\u0301' är falskt. Normalisera båda sidor med samma form innan de jämförs, och använd samma form i indexet som jämförelsen görs mot.
  • Att normalisera bara på ena sidan. En sökning som normaliserar frågan men inte det lagrade värdet hittar ingenting, och ser ut som ett dataproblem snarare än ett normaliseringsproblem.
  • Att formatera ett fält med en kompatibilitetsform och kalla det NFC-säkert. NFKC tillämpad på ett lösenord minskar utrymmet av möjliga lösenord och slår ihop indata som var avsedda att vara olika.
  • Att anta att en normaliseringsform åtgärdar en förfalskning. Det tar inte bort en nollbreddssammanfogare, en variationsväljare eller en homoglyf — ett kyrilliskt а och ett latinskt a är olika kodpunkter som NFC lämnar orörda.
  • Att lita på en teckenräkning. Antalet som en användare ser, antalet som en databaskolumn tillämpar och antalet som en byte-gräns tillämpar är tre olika tal, och raderna ovan visar hur långt isär de kan ligga.
  • Att anta att man får NFC. Text som kommer från en inmatningsmetod, en fil eller ett annat system är i den form som det systemet producerade. Den här sidan startar på NFC eftersom det är den form webben förväntas använda, inte för att det är vad inmatningen är.
Snabba svar

“Varför är dessa två strängar inte lika när de ser likadana ut?”

Eftersom en av dem skrivs med ett komponerat tecken och den andra med en bokstav plus en kombinerande markering, eller för att en av dem innehåller ett tecken som inte ritar något alls. Byt till jämförelselägett och lägg en sträng i varje ruta: sidan rapporterar den första positionen där de normaliserade strängarna skiljer sig åt och vad som finns på den positionen i vardera.

“Vilken form ska jag använda i en applikation?”

NFC för allt som en människa läser, visar, lagrar och söker i, vilket är formen W3C rekommenderar för webben. NFD är rätt svar när koden bakom vill ha basbokstäver och separata markeringar, vilket är vanligt i textbearbetning och i vissa filsystem på macOS. Använd NFKC eller NFKD endast där man matchar snarare än lagrar: sökindex, nyckelordsfilter och dubblettidentifiering.

“Är en sträng som redan är normaliserad fortfarande normaliserad efter att jag har sammanfogat den med något annat?”

Inte nödvändigtvis. Normalisering bevaras inte vid sammanfogning: att foga ihop två NFC-strängar kan ge en sekvens som NFC hade skrivit annorlunda, eftersom en kombinerande markering i början av den andra strängen kan fästa vid tecknet i slutet av den första. Normalisera efter sammansättningen, inte bara före den.

“Tar normalisering bort de osynliga tecknen?”

Nej, med ett delvis undantag: NFKC och NFKD ersätter det icke-brytande blanksteget med ett vanligt blanksteg, vilket är ett synligt resultat av en kompatibilitetsfold. Allt annat — nollbreddsmellanslag, sammanfogare, dubbelriktade styrtecken, variationsväljare — passerar alla fyra formerna orört. Det är därför det tredje läget finns.

“Varför ändras antalet byte när antalet tecken inte gör det?”

Eftersom de två formerna är uppbyggda av olika kodpunkter som kostar olika antal byte i UTF-8. Ett förkomponerat é är två byte; det dekomponerade paret är tre. Tabellen med antal ovan skriver ut alla fyra talen sida vid sida för indata och för varje form, så skillnaden syns i stället för att beskrivas.

“Hur vet jag att resultaten på den här sidan är rätta?”

Tryck på Självtest. Det normaliserar varje fall i sidans datafil, kontrollerar de fyra formerna mot varandra för de dokumenterade sambanden (NFC är idempotent, NFD för en NFC-sträng är detsamma som NFD för originalet, och så vidare), kör varje tecken som tabellerna för osynliga och kontrolltecken anger genom skannern och räknar grafemfallen mot Intl.Segmenter.

Standarder och källhänvisningar

De fyra formerna och deras definitioner kommer från Unicode Standard, bilaga 15 (Unicode Normalization Forms). Kodpunkterna som används som dekompositionsexempel är de som standarden tilldelar; den här sidan implementerar inte dekompositionstabellerna själv — den anropar webbläsarens egen String.prototype.normalize, samma implementation som plattformen använder för textinmatning, sortering och jämförelse. Det som rapporteras här är alltså vad resten av systemet gör med samma sträng. Grafemkluster kommer från Intl.Segmenter där webbläsaren har det, och från en dokumenterad approximation där den inte har det.

Sidans text skrevs för den här webbplatsen. Varje arbetat exempel på sidan skapades av sidans egen kod och kontrollerades sedan mot datafilens inspelade fallista, som i sin tur kontrollerades mot Unicodes NormalizationTest-data för samma tecken. Tabellerna för osynliga och farliga tecken är en kurerad delmängd skriven för läsbara varningar för människor, inte hela Unicodes säkerhetsprofil (UTS #39), och sidan säger det i stället för att antyda en fullständighet den inte har.