Projekt ICU (który ma teraz również bibliotekę PHP ) zawiera klasy potrzebne do normalizacji łańcuchów znaków UTF-8, aby ułatwić porównywanie wartości podczas wyszukiwania.
Jednak próbuję dowiedzieć się, co to oznacza dla aplikacji. Na przykład w jakich przypadkach chcę „Równoważność kanoniczna” zamiast „Równoważność zgodności” lub odwrotnie?
php
c
unicode
unicode-normalization
Xeoncross
źródło
źródło
(begin curved line) (char1) (char2) … (charN) (end curved line)
zamiast tego:(curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2)
. Innymi słowy, minimalna jednostka, którą można renderować?Odpowiedzi:
Wszystko, czego nigdy nie chciałeś wiedzieć o normalizacji Unicode
Normalizacja kanoniczna
Unicode obejmuje wiele sposobów kodowania niektórych znaków, w szczególności znaków akcentowanych. Normalizacja kanoniczna zmienia punkty kodowe w kanoniczną formę kodowania. Wynikowe punkty kodowe powinny wyglądać identycznie jak oryginalne, z wyjątkiem błędów w czcionkach lub silniku renderowania.
Kiedy użyć
Ponieważ wyniki wyglądają identycznie, zawsze można bezpiecznie zastosować normalizację kanoniczną do łańcucha przed jego zapisaniem lub wyświetleniem, o ile można tolerować, że wynik nie jest identyczny z bitem za bity, co dane wejściowe.
Normalizacja kanoniczna występuje w 2 formach: NFD i NFC. Obydwa są równoważne w tym sensie, że między tymi dwoma formami można przejść bez strat. Porównanie dwóch ciągów w NFC zawsze da ten sam wynik, co porównanie ich w NFD.
NFD
NFD ma postacie w pełni rozwinięte. Jest to szybsza forma normalizacji do obliczenia, ale skutkuje to większą liczbą punktów kodowych (tj. Zajmuje więcej miejsca).
Jeśli chcesz tylko porównać dwa ciągi, które nie zostały jeszcze znormalizowane, jest to preferowana forma normalizacji, chyba że wiesz, że potrzebujesz normalizacji zgodności.
NFC
NFC ponownie łączy punkty kodowe, jeśli to możliwe, po uruchomieniu algorytmu NFD. Trwa to trochę dłużej, ale skutkuje krótszymi strunami.
Normalizacja zgodności
Unicode zawiera również wiele znaków, które tak naprawdę nie należą, ale były używane w starszych zestawach znaków. Unicode dodał je, aby umożliwić przetwarzanie tekstu w tych zestawach znaków jako Unicode, a następnie konwertowanie z powrotem bez utraty.
Normalizacja zgodności konwertuje je na odpowiednią sekwencję „rzeczywistych” znaków, a także przeprowadza normalizację kanoniczną. Wyniki normalizacji zgodności mogą nie wyglądać identycznie jak oryginały.
Znaki zawierające informacje o formatowaniu są zastępowane znakami, które ich nie zawierają. Na przykład znak
⁹
zostanie przekonwertowany na9
. Inne nie obejmują różnic w formatowaniu. Na przykład cyfra rzymskaⅨ
jest konwertowana na zwykłe literyIX
.Oczywiście po wykonaniu tej transformacji nie jest już możliwa bezstratna konwersja z powrotem do oryginalnego zestawu znaków.
Kiedy użyć
Konsorcjum Unicode sugeruje myślenie o normalizacji zgodności jak o
ToUpperCase
transformacji. Jest to coś, co może się przydać w pewnych okolicznościach, ale nie powinno się jej stosować tylko chcąc nie chcąc.Doskonałym przypadkiem użycia byłaby wyszukiwarka, ponieważ prawdopodobnie chciałbyś,
9
aby pasowało zapytanie⁹
.Jedną z rzeczy, których prawdopodobnie nie powinieneś robić, jest wyświetlanie użytkownikowi wyniku zastosowania normalizacji zgodności.
NFKC / NFKD
Formularz normalizacji zgodności występuje w dwóch formach NFKD i NFKC. Mają taki sam związek jak między NFD i C.
Każdy ciąg w NFKC jest z natury także w NFC i to samo dla NFKD i NFD. Tak więc
NFKD(x)=NFD(NFKC(x))
iNFKC(x)=NFC(NFKD(x))
itd.Wniosek
W razie wątpliwości przejdź do normalizacji kanonicznej. Wybierz NFC lub NFD w oparciu o odpowiedni kompromis między przestrzenią / prędkością lub w oparciu o to, czego wymaga coś, z czym współpracujesz.
źródło
NFC(x)=Recompose(NFD(x))
.Niektóre znaki, na przykład litera z akcentem (powiedzmy
é
), można przedstawić na dwa sposoby - pojedynczy punkt kodowyU+00E9
lub zwykłą literę, po której następuje łączący znak akcentuU+0065 U+0301
. Zwykła normalizacja wybierze jedną z nich, aby zawsze ją reprezentować (pojedynczy punkt kodowy dla NFC, forma łącząca dla NFD).W przypadku znaków, które mogą być reprezentowane przez wiele sekwencji znaków podstawowych i łączonych znaków (powiedzmy „s, kropka poniżej, kropka powyżej” a umieszczenie kropki powyżej, a następnie kropka poniżej lub użycie znaku podstawowego, który już ma jedną z kropek), funkcja NFD również wybierz jedną z nich (jak to się dzieje, poniżej idzie pierwsza)
Rozkład zgodności zawiera pewną liczbę znaków, które „tak naprawdę nie powinny” być znakami, ale są, ponieważ były używane w starszych kodowaniach. Zwykła normalizacja ich nie ujednolici (aby zachować integralność w obie strony - nie jest to problem dla łączonych formularzy, ponieważ żadne starsze kodowanie [z wyjątkiem kilku wietnamskich kodowań] nie używało obu), ale normalizacja zgodności będzie. Pomyśl jak znak kilograma „kg”, który pojawia się w niektórych kodowaniach wschodnioazjatyckich (lub katakana o połówkowej / pełnej szerokości i alfabet) lub ligatura „fi” w języku MacRoman.
Więcej informacji można znaleźć pod adresem http://unicode.org/reports/tr15/ .
źródło
Formy normalne (Unicode, nie bazy danych) dotyczą głównie (wyłącznie?) Znaków ze znakami diakrytycznymi. Unicode udostępnia niektóre znaki z „wbudowanymi” znakami diakrytycznymi, na przykład U + 00C0, „Latin Capital A with Grave”. Ten sam znak może zostać utworzony z „łacińskiej dużej litery A” (U + 0041) z „łączącym poważnym akcentem” (U + 0300). Oznacza to, że nawet jeśli dwie sekwencje generują ten sam wynikowy znak, bajt po bajcie porównanie pokaże, że są zupełnie inne.
Normalizacja jest próbą rozwiązania tego problemu. Normalizacja zapewnia (lub przynajmniej próbuje), że wszystkie znaki są kodowane w ten sam sposób - albo wszystkie przy użyciu oddzielnego łączącego znaku diakrytycznego, gdy jest to konieczne, albo wszystkie przy użyciu pojedynczego punktu kodowego, jeśli to możliwe. Z punktu widzenia porównania, nie ma większego znaczenia, który wybierzesz - prawie każdy znormalizowany ciąg zostanie poprawnie porównany z innym znormalizowanym ciągiem.
W tym przypadku „zgodność” oznacza zgodność z kodem, który zakłada, że jeden punkt kodowy jest równy jednemu znakowi. Jeśli masz taki kod, prawdopodobnie chcesz użyć normalnej formy zgodności. Chociaż nigdy nie widziałem tego wprost, nazwy form normalnych sugerują, że konsorcjum Unicode uważa, że lepiej jest używać oddzielnych, łączących znaki diakrytyczne. Wymaga to większej inteligencji, aby policzyć rzeczywiste znaki w ciągu (a także takich rzeczy, jak inteligentne łamanie ciągu), ale jest bardziej uniwersalne.
Jeśli w pełni wykorzystujesz OIOM, istnieje prawdopodobieństwo, że chcesz użyć kanonicznej formy normalnej. Jeśli próbujesz samodzielnie napisać kod, który (na przykład) zakłada, że punkt kodowy jest równy znakowi, prawdopodobnie potrzebujesz normalnej formy zgodności, która sprawia, że jest to prawdą tak często, jak to możliwe.
źródło
"o\x{332}\x{303}\x{304}"
, a NFC to"\x{22D}\x{332}"
. Po drugie NFD jest"o\x{332}\x{304}\x{303}"
i NFC jest"\x{14D}\x{332}\x{303}"
. Jednak istnieje wiele niekanonicznych możliwości, które są kanonicznie równoważne z tymi. Normalizacja umożliwia binarne porównanie grafemów równoważnych kanonicznie.Jeśli dwa ciągi znaków Unicode są kanonicznie równoważne, łańcuchy są naprawdę takie same, tylko używają różnych sekwencji Unicode. Na przykład Ę można przedstawić za pomocą znaku Ę lub kombinacji A i ◌̈.
Jeśli ciągi są tylko odpowiednikami zgodności, to nie zawsze są takie same, ale mogą być takie same w niektórych kontekstach. Np. Ff można uznać za to samo, co ff.
Tak więc, jeśli porównujesz łańcuchy, powinieneś użyć równoważności kanonicznej, ponieważ równoważność zgodności nie jest prawdziwą równoważnością.
Ale jeśli chcesz posortować zestaw ciągów, sensowne może być użycie równoważności zgodności, ponieważ są one prawie identyczne.
źródło
W rzeczywistości jest to dość proste. W rzeczywistości UTF-8 ma kilka różnych reprezentacji tego samego „znaku”. (Używam znaków w cudzysłowach, ponieważ pod względem bajtów są różne, ale praktycznie są takie same). Przykład podano w połączonym dokumencie.
Znak „Ç” można przedstawić jako sekwencję bajtów 0xc387. Ale może być również reprezentowany przez
C
(0x43), po którym następuje sekwencja bajtów 0xcca7. Możesz więc powiedzieć, że 0xc387 i 0x43cca7 to ten sam znak. Powodem, który działa, jest to, że 0xcca7 to znak łączący; to znaczy, że przyjmuje znak przed nim (aC
tutaj) i modyfikuje go.Jeśli chodzi o różnicę między równoważnością kanoniczną a równoważnością zgodności, musimy ogólnie przyjrzeć się znakom.
Istnieją 2 typy znaków, te, które przekazują znaczenie poprzez wartość i te, które przyjmują inny znak i zmieniają go. 9 to znacząca postać. Superskrypt ⁹ przyjmuje to znaczenie i zmienia je poprzez prezentację. Zatem kanonicznie mają różne znaczenia, ale nadal reprezentują podstawowy charakter.
Równoważność kanoniczna występuje wtedy, gdy sekwencja bajtów renderuje ten sam znak o tym samym znaczeniu. Równoważność zgodności ma miejsce, gdy sekwencja bajtów renderuje inny znak o tym samym znaczeniu podstawowym (nawet jeśli może zostać zmieniony). 9 i ⁹ są równoważne zgodności, ponieważ oba oznaczają „9”, ale nie są równoważne kanonicznie, ponieważ nie mają tej samej reprezentacji.
źródło
To, czy równoważność kanoniczna lub równoważność zgodności jest bardziej odpowiednia dla Ciebie, zależy od aplikacji. Sposób myślenia ASCII o porównaniach ciągów z grubsza odwzorowuje równoważność kanoniczną, ale Unicode reprezentuje wiele języków. Nie sądzę, aby można było bezpiecznie założyć, że Unicode koduje wszystkie języki w sposób, który pozwala traktować je tak, jak zachodnioeuropejskie ASCII.
Rysunki 1 i 2 przedstawiają dobre przykłady obu typów równoważności. W przypadku równoważności zgodności wygląda na to, że ta sama liczba w postaci skryptu podrzędnego i nadskryptowego byłaby równa. Ale nie jestem pewien, czy rozwiązuje ten sam problem, co kursywa arabska forma lub obrócone znaki.
Trudna prawda o przetwarzaniu tekstu w standardzie Unicode polega na tym, że musisz głęboko przemyśleć wymagania dotyczące przetwarzania tekstu w aplikacji, a następnie zająć się nimi najlepiej, jak potrafisz, za pomocą dostępnych narzędzi. To nie dotyczy bezpośrednio twojego pytania, ale bardziej szczegółowa odpowiedź wymagałaby ekspertów lingwistycznych dla każdego z języków, które chcesz obsługiwać.
źródło
Problem porównywania łańcuchów : dwa łańcuchy z treścią, która jest równoważna do celów większości aplikacji, mogą zawierać różne sekwencje znaków.
Zobacz równoważność kanoniczna Unicode : jeśli algorytm porównania jest prosty (lub musi być szybki), równoważność Unicode nie jest wykonywana. Ten problem występuje na przykład w porównaniu kanonicznym XML, patrz http://www.w3.org/TR/xml-c14n
Aby uniknąć tego problemu ... Jakiego standardu użyć? „rozszerzony UTF8” czy „kompaktowy UTF8”?
Użyj „ç” lub „c + ◌̧.”?
W3C i inne (np. Nazwy plików ) sugerują użycie „skomponowanych jako kanonicznych” (weź pod uwagę C „najbardziej zwarte” krótsze ciągi) ... Więc,
Standardem jest C ! w razie wątpliwości użyj NFC
W celu zapewnienia współdziałania i wyborów typu „konwencja zamiast konfiguracji” zaleca się użycie NFC w celu „kanonizowania” zewnętrznych ciągów. Na przykład, aby przechowywać kanoniczny kod XML, należy go zapisać w „FORM_C”. CSV W3C w internetowej grupie roboczej również zaleca NFC (sekcja 7.2).
PS: de „FORM_C” jest domyślną formą w większości bibliotek. Dawny. w normalizer.isnormalized () PHP .
Terminu „ forma kompozycyjna ” (
FORM_C
) używa się zarówno do określenia „ciąg znaków w formie kanonicznej C” (będącej wynikiem transformacji NFC), jak i do stwierdzenia, że używany jest algorytm przekształcania ... Zobacz : http: //www.macchiato.com/unicode/nfc-faqUwaga: aby przetestować normalizację małych ciągów znaków (czyste odniesienia UTF-8 lub XML-encje), możesz użyć tego konwertera test / normalizacja online .
źródło