Unicode-normaliserer

Unicode har fire normaliseringsformer, og de er ikke utskiftbare. NFC og NFD flytter det samme tegnet mellom ett kodepunkt og en base pluss et kombinerende merke; NFKC og NFKD går lenger og skriver om tegn som bare ser ut som hverandre — fi-ligaturen, den fullbredde A, den omringede ①, det romerske tallet Ⅻ. Denne siden sender den samme teksten gjennom alle fire, skriver en rad per tegn som forteller hva som endret seg og hvorfor, teller grafemer mot kodepunkter og UTF-8-byte, og sammenligner to strenger under formen som velges. Den andre halvdelen handler om tegnene som aldri vises i det hele tatt: nullbreddemellomrom, sammenføyere, toveis-kontroller, variasjonsvelgere, C0- og C1-kontrolltegn, kodepunkter for privat bruk og ødelagte surrogathalvdeler, hver med posisjon og navn, og et alternativ for å fjerne dem fra en kopi av teksten.
All behandling skjer lokalt i nettleseren — data lastes aldri opp til noen server.

Usikker på hva normalisering er? Unicode-normalisering forklart på enkelt språk — hvorfor é kan være ett tegn eller to, og hvorfor det ødelegger en innlogging.
Form å rapportere og sammenligne med:
Sammenlign de to boksene etter:
NFC (ingenting ennå)
NFD (ingenting ennå)
NFKC (ingenting ennå)
NFKD (ingenting ennå)
Ingenting sammenlignet ennå. Lim inn de to strengene og trykk Sammenlign de to strengene.
første streng, normalisert (ingenting ennå)
andre streng, normalisert (ingenting ennå)
Ingenting skannet ennå. Trykk Skann etter usynlige og farlige tegn, og deretter Fjern hvis teksten uten disse tegnene ønskes.
tekst med de valgte klassene fjernet (ingenting ennå)
Ctrl+Enter i den første boksen kjører den aktive modusen.
Velg en modus og klikk handlingsknappen. Normaliser skriver alle fire formene av teksten og en rad per tegn; Sammenlign sier om to strenger er like etter normalisering; Skann lister tegnene som ikke vises, og kan fjerne dem. Selvtest-knappen kjører på nytt hvert dokumentert tilfelle i denne sidens datafil, hvert tegn de usynlige og kontrolltabellene nevner, og rapporterer antallet her.
Hvert tegn, én rad hver:hva tegnet er, hva de fire formene gjør med det, og hvorfor. En skravert rad er et tegn som minst én form endrer; NFD-kolonnen viser hva tegnet dekomponeres til når det i det hele tatt dekomponeres.
Hvor mange tegn er det?Den samme teksten telt på fire måter: grafemklynger (det en leser teller), kodepunkter (det Unicode tildeler), UTF-16-enheter (det JavaScript indekserer) og UTF-8-byte (det som sendes). Normalisering flytter tallene mellom den andre og den tredje kolonnen uten å endre den første.
Det som ble funnet, i den rekkefølgen det vises:posisjonen teller kodepunkter, klassen er den tegnet tilhører, og merknaden sier hva tegnet gjør med det som leser teksten videre.
Klassene denne siden behandler som mistenkelige:tabellen skrives ut fra skanneren selv, slik at ordene på denne siden og knappenes oppførsel ikke kan drive fra hverandre. Alvorlighetsgraden gjelder effekten på en sammenligning, ikke hvor sannsynlig det er at tegnet er uskyldig.
Kodepunkter for privat bruk listes som mistenkelige av en annen grunn enn resten: de gjengis faktisk, men de har ingen avtalt betydning, så samme kodepunkt er et annet bilde i hver skrift som definerer det.
Hvor stor inndata denne siden godtar, og hvorfor:normalisering er en ombygging av hele strengen, og denne siden gjør flere av dem pluss en skanning og en grafemtelling, så kostnaden vokser lineært med lengden på inndataene. Grensene nedenfor finnes fordi tabellen per tegn tegnes i dokumentet, og en tabell med en million rader er det som faktisk stopper en nettleser.
Hva er Unicode-normalisering?

Unicode gir mer enn én måte å skrive noen tegn på. Bokstaven é kan være ett kodepunkt, U+00E9, eller en vanlig e etterfulgt av en kombinerende akutt aksent, U+0065 U+0301. De to ser like ut på skjermen, de skrives ut likt, og som strenger er de ulike: de har ulik lengde, ulike UTF-8-byte, og 'é' === 'é' er false.

Det er ikke en kuriositet, det er en klasse feil. Om man får den ene eller den andre formen avhenger av tastaturoppsettet, operativsystemet, inndatametoden og om teksten noensinne ble kopiert gjennom et tekstbehandlingsprogram. En passordsjekk, et unikt databaseindeks, en blokkliste eller en hurtigbuffernøkkel som sammenligner strenger byte for byte, vil behandle de to skrivemåtene som to ulike verdier — så en bruker kan sette et passord på én enhet og ikke kunne logge inn fra en annen, og det samme ordet kan dukke opp to ganger i en liste over "allerede tatt"-navn.

Versjonen på 30 sekunder: NFC og NFD er den samme teksten skrevet på to måter — NFC komponerer (e + aksent blir ett tegn), NFD dekomponerer (ett tegn blir e + aksent). De skal være tapsfrie og er de to formene som brukes til sammenligning og lagring. NFKC og NFKD gjør alt det de to første gjør, og erstatter deretter også kompatibilitetstegn: ligaturen fi blir to bokstaver, ½ blir 1⁄2, det romerske tallsymbolet ⅲ blir XII, en fullbredde A blir en vanlig A. Det er akkurat det et søkeindeks eller et nøkkelordfilter ønsker, og akkurat det som aldri må brukes på et passord.
Hvorfor fire former og ikke én

Oppdelingen finnes fordi to ulike mål ble betjent. Å komponere og dekomponere tekst er en kanonisk endring: resultatet betyr det samme, og for de to første formene går ingenting tapt. Å folde bort ligaturer, hevede tall, omringede bokstaver og fullbreddeformer er en kompatibilitets-endring: den gjøres for enkelhetens skyld ved matching, og den ødelegger faktisk skillet mellom to strenger som Unicode sier er ulike. De fire formene er alle kombinasjoner av de to beslutningene.

Tabellen over er tilfellesListen fra denne sidens datafil, kontrollert mot plattformens egen String.prototype.normalize, og Selvtesten kontrollerer hver rad av den foran øynene på brukeren. Les de to første radene sammen: de er samme tekst skrevet på to måter, og etter normalisering er de samme streng.

Utarbeidede eksempler

Dette er strengene Eksempel-knappen går gjennom, normalisert direkte av siden i stedet for skrevet inn som svar. Hver enkelt ble valgt fordi en form gjør noe synlig med den.

Kompatibilitetstegn: hva NFKC koster

NFKC og NFKD er tapende, og tapet er bevisst. De besvarer spørsmålet "er dette samme ord", ikke spørsmålet "er dette samme streng". Tre konsekvenser følger, og alle tre er lett å gjøre feil i produksjonskode:

  • Lagre aldri et passord, et token eller en signatur i en kompatibilitetsform. To ulike inndata ville falle sammen til samme lagrede verdi, noe som gjør en lengde- eller tegnsjekk svakere. Normaliser passord med NFC (eller ikke i det hele tatt), og sammenlign dem som byte etter å ha gjort nøyaktig det samme på begge sider.
  • En kompatibilitetsfolding kan gjøre ett tegn til flere. Ligaturen fi er ett kodepunkt som blir to bokstaver, så et felt som ble validert for lengde før normalisering, kan bli lengre etterpå. Normaliser først, valider deretter, og lagre til slutt.
  • NFKC fjerner ikke alt som er usynlig. En variasjonsvelger overlever NFKC og NFKD, og det samme gjør en nullbreddesammenføyer. Derfor har denne siden en skannemodus: normalisering besvarer et spørsmål om et tegns form og identitet, og skanneren besvarer et spørsmål om noe i det hele tatt er der.
Grafemer, kodepunkter og byte

Normalisering endrer det andre av disse tallene og lar vanligvis det første være. De tre tallene er alle «lengden på teksten» og alle ulike, og derfor kan et skjemafelt med en grense på 20 tegn tilhøre en streng som er 20 kodepunkter, 30 UTF-16-enheter og 47 byte lang.

Tallet i grafemkolonnen er det en person teller. Gapet mellom det og antallet kodepunkter er nøyaktig det skanneren handler om: et navn bestående av 12 kodepunkter kan vises som 12 bokstaver, som 3 bokstaver med 9 usynlige merker mellom dem, eller som én emoji.

Tegnene man ikke kan se

Et nullbreddemellomrom tar ingen bredde, endrer ingen glyf, og er et ekte tegn med et ekte kodepunkt. To identifikatorer som ser helt like ut på skjermen kan skille seg med ett av dem, og det er slik et navn i en liste kan være en svindler som sorteres, søkes og autentiseres ulikt fra navnet ved siden av. Det samme gjelder toveis-kontrollene, som kan få en streng til å gjengis i en annen rekkefølge enn den ble skrevet i — et triks som er brukt til å skjule filnavn.

Skanneren i tredje modus lister hvert av dem med posisjon: nullbredde- og sammenføyertegn, toveis-kontroller, variasjonsvelgere, uvanlige mellomrom, C0- og C1-kontrolltegn, kodepunkter for privat bruk, og en surrogat som ikke er halvdelen av et gyldig par, som ikke er et tegn i det hele tatt. Klassetabellen lenger opp på siden sier hva hver klasse gjør, og fjern-knappen fjerner disse tegnene fra en kopi uten å endre inndataene.

Feilene denne siden er bygget for å fange
  • Å sammenligne to strenger uten å normalisere dem. 'é' === 'é' er falsk. Normaliser begge sider med samme form før sammenligning, og bruk samme form i indeksen man sammenligner mot.
  • Normalisering på bare én side. Et oppslag som normaliserer søket men ikke den lagrede verdien, finner ingenting, og ser ut som et datafeil snarere enn et normaliseringsproblem.
  • Å style et skjema med en kompatibilitetsform og kalle det NFC-trygt. NFKC brukt på et passord reduserer rommet av mulige passord og slår sammen inndata som skulle vært ulike.
  • Å anta at en normaliseringsform stopper et forfalskningsforsøk. Den fjerner ikke en nullbreddesammenføyer, en variasjonsvelger eller en homoglyf — en kyrillisk а og en latinsk a er ulike kodepunkter som NFC lar være urørt.
  • Å stole på en tegntelling. Tallet en bruker ser, tallet en database-kolonne håndhever og tallet en bytegrense håndhever, er tre ulike tall, og radene over viser hvor langt fra hverandre de kan være.
  • Å anta at man får NFC. Tekst som kommer fra en inndatametode, en fil eller et annet system er i den formen det systemet produserte. Denne siden starter på NFC fordi det er formen nettet forventes å bruke, ikke fordi det er det inndataene er.
Raske svar

“Hvorfor er to strenger ikke like når de ser like ut?”

Fordi den ene er skrevet med et komponert tegn og den andre med en grunnbokstav pluss et kombinerende merke, eller fordi den ene inneholder et tegn som ikke tegner noe i det hele tatt. Bytt til sammenligningsmodusen og legg én streng i hver boks: siden rapporterer den første posisjonen der de normaliserte strengene skiller seg, og hva som står på den posisjonen i hver av dem.

“Hvilken form bør jeg bruke i en applikasjon?”

NFC for alt et menneske vil lese, vise, lagre og søke i, som er formen W3C anbefaler for nettet. NFD er riktig svar når koden bak ønsker grunnbokstaver og egne merker, noe som er vanlig i tekstbehandling og i noen filsystemer på macOS. Bruk NFKC eller NFKD bare der man matcher i stedet for å lagre: søkeindekser, nøkkelordfiltre, duplikatdeteksjon.

“Forblir en streng som allerede er normalisert normalisert etter at jeg setter sammen en annen til den?”

Ikke nødvendigvis. Normalisering bevares ikke ved sammenkobling: å sette sammen to NFC-strenger kan gi en sekvens som NFC ville skrevet annerledes, fordi et kombinerende merke i starten av den andre strengen kan feste seg til tegnet på slutten av den første. Normaliser etter sammensetning, ikke bare før den.

“Fjerner normalisering de usynlige tegnene?”

Nei, med ett delvis unntak: NFKC og NFKD erstatter faktisk mellomrom uten linjeskift med et vanlig mellomrom, som er et synlig resultat av en kompatibilitetsfolding. Alt annet — nullbreddemellomrom, sammenføyere, toveis-kontroller, variasjonsvelgere — går gjennom alle fire formene uendret. Det er det tredje modusen finnes for.

“Hvorfor endrer bytetellingen seg når tegntellingen ikke gjør det?”

Fordi de to formene er bygget av ulike kodepunkter som koster ulikt antall byte i UTF-8. En ferdig sammensatt é er to byte; det dekomponerte paret er tre. Tabellen med tellinger over skriver ut alle fire tallene side om side for inndataene og for hver form, slik at forskjellen er synlig i stedet for beskrevet.

“Hvordan vet jeg at resultatene på denne siden er riktige?”

Trykk Selvtest. Den normaliserer hvert tilfelle i denne sidens datafil, sjekker de fire formene mot hverandre for de dokumenterte sammenhengene (NFC er idempotent, NFD av en NFC-streng er den samme som NFD av originalen, og så videre), kjører hvert tegn nevnt i de usynlige og kontrolltabellene gjennom skanneren, og teller grafemtilfellene mot Intl.Segmenter.

Standarder og kreditering

De fire formene og deres definisjoner er fra Unicode Standard, Annex 15 (Unicode Normalization Forms). Kodepunktene som brukes som dekomponeringseksempler, er de standarden tildeler; denne siden implementerer ikke dekomponeringstabellene selv — den kaller nettleserens egen String.prototype.normalize, som er den samme implementasjonen plattformen bruker for tekstinndata, sortering og sammenligning, slik at det som rapporteres her, er det resten av systemet gjør med den samme strengen. Grafemklynger kommer fra Intl.Segmenter der nettleseren har den, og fra en dokumentert tilnærming der den ikke har det.

Sidens tekst er skrevet for dette nettstedet. Hvert arbeidseksempel på denne siden ble produsert av sidens egen kode og deretter kontrollert mot datafilens registrerte tilfellesliste, som igjen ble kontrollert mot Unicodes NormalizationTest-data for de samme tegnene. De usynlige og farlige tegntabellene er et kuratert utvalg skrevet for lesbare advarsler, ikke hele Unicodes sikkerhetsprofil (UTS #39), og siden sier det i stedet for å antyde en fullstendighet den ikke har.