Normalizátor Unicode

Unicode má čtyři normalizační tvary a nejsou zaměnitelné. NFC a NFD přeskládají týž znak mezi jedním kódovým bodem a základem plus kombinující znaménko; NFKC a NFKD jdou dál a přepisují znaky, které se jen podobají jiným — ligaturu fi, celoširokoúhlé A, kroužkované ①, římskou číslici Ⅻ. Tato stránka prožene týž text všemi čtyřmi, vypíše řádek na znak s tím, co se změnilo a proč, porovná grafémy s kódovými body a bajty UTF-8 a porovná dva řetězce podle zvoleného tvaru. Druhá polovina se týká znaků, které se vůbec neukážou: nulové šířky mezery, spojovníky, obousměrná řízení, selektory variant, řídicí znaky C0 a C1, kódové body soukromého použití a rozbité poloviny surrogátů, každý s pozicí a názvem, a možností je z kopie textu odstranit.
Veškeré zpracování probíhá lokálně ve vašem prohlížeči — vaše data se nikdy nenahrávají na žádný server.

Nejste si jisti, co je normalizace? Normalizace Unicode vysvětlena prostě — proč může být é jeden znak nebo dva a proč to rozbije přihlášení.
Tvar, který se hlásí a podle kterého se porovnává:
Porovnat obě pole po:
NFC (zatím nic)
NFD (zatím nic)
NFKC (zatím nic)
NFKD (zatím nic)
Zatím nic neporovnáno. Vložte oba řetězce a stiskněte Porovnat dva řetězce.
první řetězec, normalizovaný (zatím nic)
druhý řetězec, normalizovaný (zatím nic)
Zatím nic neprohledáno. Stiskněte Prohledat neviditelné a nebezpečné znaky a pak Odstranit, pokud chcete text bez těchto znaků.
text s odstraněnými vybranými třídami (zatím nic)
Ctrl+Enter v prvním poli spustí aktivní režim.
Vyberte režim a klikněte na jeho akční tlačítko. Normalizovat zapíše všechny čtyři tvary vašeho textu a řádek na znak; Porovnat řekne, zda jsou dva řetězce po normalizaci shodné; Prohledat vypíše znaky, které se nezobrazí, a může je odstranit. Tlačítko Samotest znovu spustí každý zdokumentovaný případ v datovém souboru této stránky, každý znak z tabulek neviditelných a řídicích znaků a zde uvede počet.
Každý znak, jeden řádek na znak:co znak je, co s ním čtyři tvary udělají a proč. Zastíněný řádek je znak, který alespoň jeden tvar mění; sloupec NFD ukazuje, na co se znak rozloží, pokud se vůbec rozkládá.
Kolik to je znaků?Týž text spočítaný čtyřmi způsoby: grafémové shluky (co čtenář počítá), kódové body (co Unicode přiřazuje), jednotky UTF-16 (co JavaScript indexuje) a bajty UTF-8 (co se přenáší). Normalizace přesouvá čísla mezi druhým a třetím sloupcem, aniž by změnila první.
Co bylo nalezeno, v pořadí výskytu:pozice počítá kódové body, třída je ta, do které znak patří, a poznámka říká, co ten znak udělá s tím, co text čte dál.
Třídy, které tato stránka považuje za podezřelé:Tabulka se tiskne přímo z prohledávače, takže slova na této stránce a chování tlačítek se nemohou rozejít. Závažnost se týká účinku na porovnání, ne toho, jak pravděpodobné je, že je znak nevinný.
Znaky soukromého použití jsou označeny jako podezřelé z jiného důvodu než ostatní: vykreslí se, jen nemají dohodnutý význam, takže týž kódový bod je v každém písmu, které jej definuje, jiný obrázek.
Jak velký vstup tato stránka přijme a proč:normalizace je přestavba celého řetězce a tato stránka jich provádí několik plus prohledání a počítání grafémů, takže náklady rostou přímo úměrně délce vstupu. Níže uvedené limity existují, protože tabulka po znacích se kreslí v dokumentu, a tabulka s milionem řádků je přesně to, co prohlížeč zastaví.
Co je normalizace Unicode?

Unicode dává pro některé znaky více než jeden způsob zápisu. Písmeno é může být jeden kódový bod, U+00E9, nebo obyčejné e následované kombinujícím akutem, U+0065 U+0301. Na obrazovce vypadají stejně, vytisknou se stejně, ale jako řetězce jsou různé: mají různé délky, různé bajty UTF-8 a 'é' === 'e\u0301' je false.

To není kuriozita, je to třída chyb. Který tvar dostanete, závisí na rozložení klávesnice, operačním systému, vstupní metodě a na tom, zda byl text někdy zkopírován přes textový procesor. Kontrola hesla, unikátní index databáze, blokovací seznam nebo klíč cache, který porovnává řetězce bajt po bajtu, bude oba pravopisy považovat za dvě různé hodnoty — uživatel tak může nastavit heslo na jednom zařízení a nemoci se přihlásit z jiného a totéž slovo se může v seznamu „již obsazených“ jmen objevit dvakrát.

Verze na 30 sekund: NFC a NFD jsou týž text zapsaný dvěma způsoby — NFC skládá (e + akcent se stane jedním znakem), NFD rozkládá (jeden znak se stane e + akcent). Mají být bezeztrátové a jsou to dva tvary, které se používají k porovnání a ukládání. NFKC a NFKD dělají totéž co první dva a navíc nahrazují kompatibilní znaky: ligatura fi se změní na dvě písmena, ½ na 1⁄2, římská číslice ⅲ na XII, celoširokoúhlé A na obyčejné A. Přesně to chce vyhledávací index nebo filtr klíčových slov a přesně to nikdy nesmíte použít na heslo.
Proč čtyři tvary a ne jeden

Rozdělení existuje, protože se sledovaly dva různé cíle. Skládání a rozklad textu je kanonická změna: výsledek znamená totéž a u prvních dvou tvarů se nic neztrácí. Sloučení ligatur, horních indexů, kroužkovaných písmen a celoširokoúhlých tvarů je kompatibilní změna: dělá se pro pohodlí porovnávání a skutečně ruší rozdíl mezi dvěma řetězci, které Unicode označuje za různé. Čtyři tvary jsou všechny kombinace obou rozhodnutí.

Tabulka výše je seznam případů z datového souboru této stránky, ověřený proti vlastnímu String.prototype.normalize platformy, a Samotest každý její řádek znovu ověří přímo před vámi. Čtěte první dva řádky společně: jsou to týž text zapsaný dvěma způsoby a po normalizaci jsou týž řetězec.

Propracované příklady

Toto jsou řetězce, kterými provede tlačítko Ukázka, a stránka je normalizuje živě, místo aby byly napsány jako odpovědi. Každý byl vybrán proto, že na něj některý tvar udělá něco viditelného.

Znaky s kompatibilitou: co vás stojí NFKC

NFKC a NFKD jsou ztrátové a ztráta je záměrná. Odpovídají na otázku „je to stejné slovo“, ne na otázku „je to stejný řetězec“. Z toho plynou tři důsledky, a všechny tři se v produkčním kódu snadno pokazí:

  • Nikdy neukládejte heslo, token nebo podpis v kompatibilním tvaru. Dva různé vstupy by se sloučily do téže uložené hodnoty, což kontrolu délky nebo znaků oslabí. Hesla normalizujte NFC (nebo vůbec) a porovnávejte je jako bajty poté, co totéž uděláte na obou stranách.
  • Kompatibilní sloučení může jeden znak proměnit v několik. Ligatura fi je jeden kódový bod, který se změní na dvě písmena, takže pole ověřené na délku před normalizací může být po ní delší. Nejprve normalizujte, pak ověřte, pak ukládejte.
  • NFKC neodstraní všechno neviditelné. Selektor varianty přežije NFKC i NFKD a stejně tak spojovník nulové šířky. Proto má tato stránka režim prohledávání: normalizace odpovídá na otázku o tvaru a identitě znaku, prohledávač odpovídá na otázku, zda tam něco vůbec je.
Grafémy, kódové body a bajty

Normalizace mění druhý z těchto počtů a obvykle první nechává být. Všechna tři čísla jsou „délka textu“ a všechna jsou různá, a proto může políčko formuláře s limitem 20 znaků patřit řetězci, který má 20 kódových bodů, 30 jednotek UTF-16 a 47 bajtů.

Číslo ve sloupci grafémů je to, co člověk počítá. Rozdíl oproti počtu kódových bodů je přesně to, čím se prohledávač zabývá: jméno složené z 12 kódových bodů se může zobrazit jako 12 písmen, jako 3 písmena s 9 neviditelnými znaménky mezi nimi, nebo jako jeden emotikon.

Znaky, které nevidíte

Nulová šířka mezery nezabírá žádnou šířku, nemění glyf a je to skutečný znak se skutečným kódovým bodem. Dva identifikátory, které na obrazovce vypadají stejně, se mohou lišit jedním z nich; právě tak může být jméno v seznamu podvržené, které se třídí, hledá a ověřuje jinak než jméno vedle něj. Totéž platí pro obousměrná řízení, která mohou způsobit, že se řetězec zobrazí v jiném pořadí, než v jakém byl napsán — trik, kterým se maskovaly názvy souborů.

Prohledávač v třetím režimu vypíše každý z nich s pozicí: nulové šířky a spojovníky, obousměrná řízení, selektory variant, neobvyklé mezery, řídicí znaky C0 a C1, kódové body soukromého použití a surrogát, který není polovinou platné dvojice a vůbec není znakem. Tabulka tříd výše na této stránce říká, co která třída dělá, a tlačítko odstranění odstraní kopii těchto znaků, aniž se dotkne vašeho vstupu.

Chyby, na které je tato stránka postavena, aby je zachytila
  • Porovnání dvou řetězců bez normalizace. 'é' === 'e\u0301' je nepravda. Normalizujte obě strany týmž tvarem před porovnáním a použijte týž tvar i v indexu, proti kterému porovnáváte.
  • Normalizace jen na jedné straně. Vyhledávání, které normalizuje dotaz, ale ne uloženou hodnotu, nic nenajde a vypadá jako problém s daty, ne s normalizací.
  • Zacházení s kompatibilním tvarem jako s bezpečným pro NFC. NFKC aplikované na heslo zkracuje prostor možných hesel a slučuje vstupy, které měly být různé.
  • Předpoklad, že normalizační tvar opraví podvrh. Neodstraní nulovou šířku spojovníku, selektor varianty ani homoglyf — azbučné а a latinské a jsou různé kódové body, které NFC nechá být.
  • Důvěra v počet znaků. Počet, který vidí uživatel, počet, který vynucuje sloupec databáze, a počet, který vynucuje limit bajtů, jsou tři různá čísla a řádky výše ukazují, jak daleko od sebe mohou být.
  • Předpoklad, že dostanete NFC. Text, který přijde ze vstupní metody, ze souboru nebo z jiného systému, je v tom tvaru, který vyprodukoval ten systém. Tato stránka začíná na NFC, protože je to tvar, který web očekává, ne proto, že by to byl tvar vašeho vstupu.
Rychlé odpovědi

“Proč tyto dva řetězce nejsou shodné, když vypadají stejně?”

Protože jeden z nich je napsán složeným znakem a druhý základním písmenem plus kombinujícím znaménkem, nebo jeden z nich obsahuje znak, který se vůbec nekreslí. Přepněte do režimu Porovnat a dejte jeden řetězec do každého pole: stránka nahlásí první pozici, kde se normalizované řetězce liší, a co je na této pozici v každém z nich.

“Který tvar mám použít v aplikaci?”

NFC pro cokoli, co člověk přečte, zobrazí, uloží a bude hledat; je to tvar, který W3C doporučuje pro web. NFD je správná volba, když kód za vámi chce základní písmena a samostatná znaménka, což je běžné ve zpracování textu a v některých souborových systémech na macOS. NFKC nebo NFKD používejte jen tam, kde porovnáváte, ne ukládáte: vyhledávací indexy, filtry klíčových slov, detekce duplicit.

“Zůstane řetězec, který je už normalizovaný, normalizovaný i poté, co k němu něco připojím?”

Ne nutně. Normalizace se nezachovává zřetězením: spojení dvou řetězců NFC může vytvořit sekvenci, kterou by NFC zapsalo jinak, protože kombinující znaménko na začátku druhého řetězce se může připojit k znaku na konci prvního. Normalizujte až po sestavení, ne jen před ním.

“Odstraňuje normalizace neviditelné znaky?”

Ne, s jednou částečnou výjimkou: NFKC a NFKD nahrazují mezeru bez zalomení běžnou mezerou, což je viditelný výsledek kompatibilního sloučení. Vše ostatní — nulové šířky mezery, spojovníky, obousměrná řízení, selektory variant — projde všemi čtyřmi tvary nedotčeno. Právě pro to existuje třetí režim.

“Proč se mění počet bajtů, když se počet znaků nemění?”

Protože oba tvary jsou složené z různých kódových bodů, které v UTF-8 stojí různý počet bajtů. Předskládané é jsou dva bajty; rozložená dvojice tři. Tabulka počtů výše vypisuje všechna čtyři čísla vedle sebe pro vstup a pro každý tvar, takže rozdíl je vidět, a ne jen popsán.

“Jak poznám, že výsledky na této stránce jsou správné?”

Stiskněte Samotest. Normalizuje každý případ v datovém souboru této stránky, znovu ověří čtyři tvary proti sobě podle zdokumentovaných vztahů (NFC je idempotentní, NFD normalizovaného NFC je totéž jako NFD původního atd.), prožene každý znak uvedený v tabulkách neviditelných a řídicích znaků prohledávačem a porovná případy grafémů s Intl.Segmenter.

Normy a uznání

Čtyři tvary a jejich definice pocházejí z Unicode Standard, Annex 15 (Unicode Normalization Forms). Kódové body použité jako příklady rozkladu jsou ony, které standard přiřazuje; tato stránka nepřináší vlastní tabulky rozkladu — volá vlastní String.prototype.normalize prohlížeče, tedy tutéž implementaci, kterou platforma používá pro zadávání textu, řazení a porovnávání, takže zde hlášené je to, co s týmž řetězcem dělá zbytek systému. Grafémové shluky pocházejí z Intl.Segmenter, je-li v prohlížeči k dispozici, a jinak ze zdokumentované aproximace.

Text stránky byl napsán pro tento web. Každý propracovaný příklad na této stránce vytvořil kód stránky a poté byl ověřen proti zaznamenanému seznamu případů datového souboru, který byl zase ověřen proti datům Unicode NormalizationTest pro tytéž znaky. Tabulky neviditelných a nebezpečných znaků jsou kurátorská podmnožina psaná pro varování čitelná člověkem, ne úplný bezpečnostní profil Unicode (UTS #39), a stránka to říká, místo aby naznačovala úplnost, kterou nemá.