Normalizator Unicode

Unicode ma cztery postacie normalizacji i nie są one wymienne. NFC i NFD przestawiają ten sam znak między jednym punktem kodowym a znakiem podstawowym z połączonym znakiem diakrytycznym; NFKC i NFKD idą dalej i przepisują znaki, które tylko wyglądają podobnie — ligaturę fi, pełnoszerokościowe A, kółkowe ①, cyfrę rzymską Ⅻ. Ta strona przepuszcza ten sam tekst przez wszystkie cztery, wypisuje wiersz na każdy znak z opisem, co się zmieniło i dlaczego, zlicza grafemy względem punktów kodowych i bajtów UTF-8 oraz porównuje dwa ciągi w wybranej postaci. Druga połowa dotyczy znaków, które w ogóle się nie pokazują: zerowej szerokości spacji, łączników, sterowań dwukierunkowych, selektorów wariantów, znaków sterujących C0 i C1, punktów kodowych prywatnego użytku i uszkodzonych połówek par zastępczych, każdy z pozycją i nazwą, oraz opcją usunięcia ich z kopii tekstu.
Całe przetwarzanie odbywa się lokalnie w przeglądarce — dane nigdy nie są wysyłane na żaden serwer.

Nie wiadomo, czym jest normalizacja? Normalizacja Unicode wyjaśniona zwykłym językiem — dlaczego é może być jednym znakiem albo dwoma i dlaczego to psuje logowanie.
Postać do raportowania i porównywania:
Porównaj dwa pola po zastosowaniu:
NFC (jeszcze nic)
NFD (jeszcze nic)
NFKC (jeszcze nic)
NFKD (jeszcze nic)
Nic jeszcze nie porównano. Wklej dwa ciągi i naciśnij Porównaj dwa ciągi.
pierwszy ciąg, znormalizowany (jeszcze nic)
drugi ciąg, znormalizowany (jeszcze nic)
Nic jeszcze nie przeskanowano. Naciśnij Skanuj w poszukiwaniu niewidocznych i niebezpiecznych znaków, a potem Usuń je, jeśli ma być tekst bez tych znaków.
tekst z usuniętymi wybranymi klasami (jeszcze nic)
Ctrl+Enter w pierwszym polu uruchamia aktywny tryb.
Wybierz tryb i kliknij jego przycisk akcji. Normalizuj zapisuje wszystkie cztery postacie tekstu i wiersz na każdy znak; Porównaj mówi, czy dwa ciągi są takie same po normalizacji; Skanuj wypisuje znaki, które nie są widoczne, i może je usunąć. Przycisk Autotest uruchamia ponownie każdy udokumentowany przypadek z pliku danych tej strony, każdy znak z tabel niewidocznych i kontrolnych, i podaje tutaj ich liczbę.
Każdy znak, po jednym wierszu:czym jest znak, co robią z nim cztery postacie i dlaczego. Zacieniony wiersz to znak, który zmienia co najmniej jedna postać; kolumna NFD pokazuje, na co znak się rozkłada, jeśli w ogóle się rozkłada.
Ile to znaków?Ten sam tekst policzony czterema sposobami: klastry grafemów (to, co liczy czytelnik), punkty kodowe (to, co przypisuje Unicode), jednostki UTF-16 (to, co indeksuje JavaScript) i bajty UTF-8 (to, co przesyła się dalej). Normalizacja przesuwa liczby między drugą a trzecią kolumną, nie zmieniając pierwszej.
Co znaleziono, w kolejności występowania:pozycja liczy punkty kodowe, klasa to ta, do której należy znak, a uwaga mówi, co ten znak robi z tekstem czytanym dalej.
Klasy, które ta strona traktuje jako podejrzane:tabela jest drukowana z samego skanera, więc słowa na tej stronie i działanie przycisków nie mogą się rozjechać. Istotność dotyczy wpływu na porównanie, a nie tego, jak prawdopodobne jest, że znak jest niewinny.
Znaki prywatnego użytku są oznaczane jako podejrzane z innego powodu niż reszta: rzeczywiście się wyświetlają, po prostu nie mają uzgodnionego znaczenia, więc ten sam punkt kodowy wygląda inaczej w każdej czcionce, która go definiuje.
Jak duże wejście strona przyjmuje i dlaczego:normalizacja to przebudowa całego ciągu, a ta strona wykonuje ich kilka oraz skan i zliczanie grafemów, więc koszt rośnie liniowo wraz z długością wejścia. Poniższe limity istnieją, bo tabela znak po znaku jest rysowana w dokumencie, a tabela z milionem wierszy to właśnie to, co realnie zatrzymuje przeglądarkę.
Czym jest normalizacja Unicode?

Unicode podaje więcej niż jeden sposób zapisu niektórych znaków. Litera é może być pojedynczym punktem kodowym, U+00E9, albo zwykłym e z następującym akcentem łączącym, U+0065 U+0301. Na ekranie wyglądają tak samo, drukują się tak samo, a jako ciągi są różne: mają różną długość, różne bajty UTF-8, a 'é' === 'e\u0301' daje false.

To nie jest ciekawostka, tylko klasa błędów. To, którą postać dostanie się, zależy od układu klawiatury, systemu, metody wprowadzania i tego, czy tekst kiedykolwiek przeszedł przez edytor tekstu. Sprawdzenie hasła, unikalny indeks w bazie, lista blokowanych słów albo klucz pamięci podręcznej, które porównują ciągi bajt po bajcie, potraktują obie pisownie jako dwie różne wartości — więc użytkownik może ustawić hasło na jednym urządzeniu i nie zalogować się z innego, a to samo słowo może pojawić się dwa razy na liście nazw „już zajętych”.

Wersja na 30 sekund: NFC i NFD to ten sam tekst zapisany na dwa sposoby — NFC składa (e + akcent staje się jednym znakiem), NFD rozkłada (jeden znak staje się e + akcent). Mają być bezstratne i są dwiema postaciami, których należy używać do porównań i przechowywania. NFKC i NFKD robią wszystko, co dwie pierwsze, a potem zastępują także znaki zgodności: ligatura fi staje się dwiema literami, ½ staje się 1⁄2, cyfra rzymska ⅲ staje się XII, znak pełnej szerokości A staje się zwykłym A. Dokładnie tego chce indeks wyszukiwania albo filtr słów kluczowych i dokładnie tego nigdy nie wolno stosować do hasła.
Dlaczego cztery postacie, a nie jedna

Podział istnieje, bo służą dwa różne cele. Składanie i rozkładanie tekstu to zmiana kanoniczna: wynik znaczy to samo i przy dwóch pierwszych postaciach nic nie ginie. Zwijanie ligatur, indeksów górnych, liter w kółkach i postaci pełnej szerokości to zmiana zgodności: wykonuje się ją dla wygody dopasowywania, a ona rzeczywiście niszczy rozróżnienie między dwoma ciągami, które Unicode uznaje za różne. Cztery postacie to wszystkie kombinacje obu decyzji.

Tabela powyżej to lista przypadków z pliku danych tej strony, sprawdzona względem natywnego String.prototype.normalize platformy; Autotest ponownie sprawdza każdy wiersz na bieżąco. Pierwsze dwa wiersze czyta się razem: to ten sam tekst zapisany na dwa sposoby, a po normalizacji są tym samym ciągiem.

Przykłady

To są ciągi, które przechodzi przycisk Przykład, normalizowane na bieżąco przez stronę, a nie wpisane jako odpowiedzi. Każdy został wybrany, bo któraś z postaci robi z nim coś widocznego.

Znaki zgodności: co kosztuje NFKC

NFKC i NFKD są stratne, a strata jest zamierzona. Odpowiadają na pytanie „czy to to samo słowo”, a nie na pytanie „czy to ten sam ciąg”. Z tego wynikają trzy konsekwencje, które łatwo pomylić w kodzie produkcyjnym:

  • Nigdy nie przechowuj hasła, tokena ani podpisu w postaci zgodności. Dwa różne wejścia zwinęłyby się do tej samej zapisanej wartości, co zamienia kontrolę długości lub znaków w słabszą. Normalizuj hasła przez NFC (albo wcale) i porównuj je jako bajty po zrobieniu dokładnie tego samego z obu stron.
  • Składanie zgodności może zamienić jeden znak w kilka. Ligatura fi to jeden punkt kodowy, który staje się dwiema literami, więc pole sprawdzone pod kątem długości przed normalizacją może być po niej dłuższe. Najpierw normalizuj, potem waliduj, a dopiero potem zapisuj.
  • NFKC nie usuwa wszystkiego, czego nie widać. Selektor wariantu przetrwa NFKC i NFKD, tak samo łącznik zerowej szerokości. Dlatego ta strona ma tryb skanowania: normalizacja odpowiada na pytanie o kształt i tożsamość znaku, a skaner odpowiada na pytanie, czy w ogóle coś tam jest.
Grafemy, punkty kodowe i bajty

Normalizacja zmienia drugą z tych liczb i zwykle zostawia pierwszą w spokoju. Wszystkie trzy liczby to „długość tekstu” i wszystkie są różne, dlatego pole formularza z limitem 20 znaków może zawierać ciąg, który ma 20 punktów kodowych, 30 jednostek UTF-16 i 47 bajtów.

Liczba w kolumnie grafemów to to, co liczy człowiek. Różnica między nią a liczbą punktów kodowych jest dokładnie tym, czym zajmuje się skaner: nazwa złożona z 12 punktów kodowych może wyświetlać się jako 12 liter, jako 3 litery z 9 niewidocznymi znakami między nimi albo jako jedno emoji.

Znaki, których nie widać

Zerowej szerokości spacja nie zajmuje miejsca, nie zmienia glifu i jest prawdziwym znakiem z prawdziwym punktem kodowym. Dwa identyfikatory, które na ekranie wyglądają identycznie, mogą różnić się jedną taką spacją; tak nazwa na liście może być oszustem, który sortuje, wyszukuje i uwierzytelnia się inaczej niż nazwa obok. To samo dotyczy sterowań dwukierunkowych, które mogą sprawić, że ciąg zostanie wyrenderowany w innej kolejności niż zapisano — sztuczka używana do maskowania nazw plików.

Skaner w trzecim trybie wypisuje każdy z nich wraz z pozycją: znaki zerowej szerokości i łączniki, sterowania dwukierunkowe, selektory wariantów, nietypowe spacje, znaki sterujące C0 i C1, punkty kodowe prywatnego użytku oraz elementy par zastępczych, które nie są połową prawidłowej pary, a więc w ogóle nie są znakiem. Tabela klas wyżej na tej stronie opisuje, co robi każda klasa, a przycisk usuwania usuwa kopię tych znaków, nie ruszając wejścia.

Błędy, na które ta strona została zbudowana
  • Porównywanie dwóch ciągów bez ich normalizacji. 'é' === 'e\u0301' daje false. Znormalizuj obie strony tą samą postacią przed porównaniem i używaj tej samej postaci w indeksie, z którym się porównuje.
  • Normalizowanie tylko po jednej stronie. Wyszukiwanie, które normalizuje zapytanie, a nie zapisaną wartość, nic nie znajduje i wygląda na problem z danymi, a nie z normalizacją.
  • Stosowanie postaci zgodności i nazywanie tego bezpiecznym dla NFC. NFKC zastosowane do hasła skraca przestrzeń możliwych haseł i łączy wejścia, które miały być różne.
  • Zakładanie, że postać normalizacji usuwa podszywanie. Nie usuwa zerowej szerokości łącznika, selektora wariantu ani homoglifu — cyrylicka а i łacińska a to różne punkty kodowe, których NFC nie rusza.
  • Ufanie liczbie znaków. Liczba, którą widzi użytkownik, liczba wymuszana przez kolumnę bazy danych i liczba wymuszana przez limit bajtów to trzy różne liczby, a wiersze powyżej pokazują, jak bardzo mogą się różnić.
  • Zakładanie, że tekst jest w NFC. Tekst przychodzący z metody wprowadzania, pliku albo innego systemu ma postać, którą wytworzył tamten system. Ta strona zaczyna od NFC, bo tego oczekuje się od sieci, a nie dlatego, że taką postać ma wejście.
Krótkie odpowiedzi

„Dlaczego te dwa ciągi nie są równe, skoro wyglądają tak samo?”

Ponieważ jeden z nich jest zapisany znakiem złożonym, a drugi literą podstawową z połączonym znakiem diakrytycznym, albo jeden zawiera znak, który w ogóle niczego nie rysuje. Przełącz się na tryb Porównanie i wpisz po jednym ciągu w każdym polu: strona podaje pierwszą pozycję, na której znormalizowane ciągi się różnią, oraz to, co znajduje się na tej pozycji w każdym z nich.

„Jakiej postaci powinienem użyć w aplikacji?”

NFC do wszystkiego, co człowiek przeczyta, wyświetli, zapisze i przeszuka; to postać, którą W3C zaleca dla sieci. NFD jest właściwą odpowiedzią, gdy kod po drugiej stronie chce znaków podstawowych i osobnych znaków diakrytycznych, co jest częste w przetwarzaniu tekstu i w niektórych systemach plików na macOS. NFKC lub NFKD stosuj tylko tam, gdzie dopasowuje się, a nie zapisuje się: indeksy wyszukiwania, filtry słów kluczowych, wykrywanie duplikatów.

„Czy ciąg już znormalizowany pozostaje znormalizowany po dodaniu do niego tekstu?”

Niekoniecznie. Normalizacja nie jest zachowywana przy łączeniu: złączenie dwóch ciągów w NFC może dać sekwencję, którą NFC zapisałby inaczej, bo znak diakrytyczny na początku drugiego ciągu może przyczepić się do znaku na końcu pierwszego. Normalizuj po złożeniu, a nie tylko przed nim.

„Czy normalizacja usuwa niewidoczne znaki?”

Nie, z jednym częściowym wyjątkiem: NFKC i NFKD zastępują spację niełamiącą zwykłą spacją, co jest widocznym skutkiem składania zgodności. Cała reszta — zerowej szerokości spacje, łączniki, sterowania dwukierunkowe, selektory wariantów — przechodzi przez wszystkie cztery postacie bez zmian. Właśnie do tego służy trzeci tryb.

„Dlaczego liczba bajtów się zmienia, skoro liczba znaków nie?”

Ponieważ obie postacie są zbudowane z różnych punktów kodowych, które w UTF-8 zajmują różną liczbę bajtów. Gotowe é to dwa bajty; rozłożona para to trzy. Tabela liczników powyżej wypisuje wszystkie cztery liczby obok siebie dla wejścia i dla każdej postaci, więc różnica jest widoczna, a nie tylko opisana.

„Skąd mam wiedzieć, że wyniki na tej stronie są poprawne?”

Naciśnij Autotest. Normalizuje on każdy przypadek z pliku danych tej strony, ponownie sprawdza cztery postacie względem siebie pod kątem udokumentowanych zależności (NFC jest idempotentne, NFD ciągu NFC jest takie samo jak NFD oryginału itd.), przepuszcza przez skaner każdy znak nazwany w tabelach niewidocznych i kontrolnych oraz porównuje przypadki grafemów z Intl.Segmenter.

Standardy i autorstwo

Cztery postacie i ich definicje pochodzą z Unicode Standard, Annex 15 (Unicode Normalization Forms). Punkty kodowe użyte jako przykłady rozkładu to te, które standard przypisuje; ta strona nie implementuje sama tablic rozkładu — wywołuje natywne String.prototype.normalize przeglądarki, czyli tę samą implementację, której platforma używa do wprowadzania tekstu, sortowania i porównywania, więc podawany tu wynik to dokładnie to, co reszta systemu zrobi z tym samym ciągiem. Klastry grafemów pochodzą z Intl.Segmenter, tam gdzie przeglądarka go ma, a z udokumentowanego przybliżenia tam, gdzie nie ma.

Tekst strony został napisany dla tego serwisu. Każdy przykład na tej stronie został wytworzony przez kod strony, a następnie sprawdzony względem zarejestrowanej w pliku danych listy przypadków, która z kolei została sprawdzona względem danych NormalizationTest Unicode dla tych samych znaków. Tabele niewidocznych i niebezpiecznych znaków to wyselekcjonowany podzbiór przeznaczony do czytelnych dla człowieka ostrzeżeń, a nie pełny profil bezpieczeństwa Unicode (UTS #39), i strona to mówi wprost, zamiast sugerować kompletność, której nie ma.