UTF-8 (skrót od Universal Character Set Transformation Format - 8 bits ) to komputerowe kodowanie znaków przeznaczone do kodowania zestawu znaków z „uniwersalnego repertuaru znaków kodowanych”, pierwotnie opracowanego przez ISO w standardzie międzynarodowym ISO/IEC 10646 , teraz w pełni kompatybilny ze standardem Unicode , pozostając jednocześnie kompatybilnym ze standardem ASCII ograniczonym do podstawowego języka angielskiego, ale szeroko stosowanym od dziesięcioleci.
UTF-8 jest używany przez 82,2% witryn w grudzień 2014, 87,6% w 2016, 90,5% w 2017, 93,1% w luty 2019 i prawie 95,2% w październik 2020. Ze swej natury UTF-8 jest coraz częściej używany w Internecie oraz w systemach, które muszą wymieniać informacje. Jest to również najczęściej używane kodowanie w systemach GNU , Linux i kompatybilnych do zarządzania tekstami i ich tłumaczeniami tak prosto, jak to możliwe we wszystkich systemach pisma i we wszystkich alfabetach na świecie.
UTF-8 to „format transformacji” wywodzący się z pracy dla standardu ISO/IEC 10646 , tj. UTF-8 definiuje kodowanie dla dowolnego skalarnego punktu kodowego ( znak abstrakcyjny lub „nieznakowy”) z uniwersalnego zestawu znaków ( UCS ) informator. Katalog ten jest teraz wspólny dla standardu ISO/IEC 10646 (od jego wersji 1) oraz dla standardu Unicode (od wersji 1.1).
UTF-8 jest oficjalnie zdefiniowany w standardzie ISO/IEC 10646 od momentu jego przyjęcia w poprawce opublikowanej w 1996 roku. Został również opisany w standardzie Unicode i jest częścią tego standardu od wersji 3.0 opublikowanej w 2000 roku. W 1996 roku został opublikowany RFC 2044 („ UTF-8, format transformacji ISO 10646 ”) w celu zapewnienia przystępnej specyfikacji UTF-8 i rozpoczęcia jego standaryzacji w ramach Internet Engineering Task Force (IETF). Ten dokument RFC został zaktualizowany w 1998 ( RFC 2279) i wreszcie w 2003 ( RFC 3629), przy czym ta ostatnia wersja uczyniła UTF-8 jednym ze standardów Internetu (STD 63).
Z technicznego punktu widzenia obejmuje to kodowanie znaków Unicode w postaci sekwencji od jednego do czterech punktów kodowych, każdy po jednym bajcie . Standard Unicode definiuje między innymi zbiór (lub katalog) znaków. Każdy znak w tym zestawie jest identyfikowany przez cały indeks zwany również „ punktem kodowym ”. Na przykład znak „€” ( euro ) jest 8365- tym znakiem katalogu Unicode, jego indeksem lub punktem kodowym jest zatem 8364 (0x20AC) (zaczynamy liczyć od 0).
Katalog Unicode może zawierać ponad milion znaków, co jest zbyt duże, aby można je było zakodować jako pojedynczy bajt (ograniczony do wartości od 0 do 255). Standard Unicode definiuje zatem znormalizowane metody kodowania i przechowywania tego indeksu w postaci ciągu bajtów: UTF-8 jest jednym z nich, wraz z UTF-16 , UTF-32 i ich różnymi wariantami.
Główną cechą UTF-8 jest to, że jest wstecznie kompatybilny ze standardem ASCII, to znaczy, że każdy znak ASCII jest zakodowany w UTF-8 w postaci jednego bajtu, identycznego z kodem ASCII. Na przykład „A” (wielka litera A) ma kod ASCII 65 (0x41) i jest zakodowany w UTF-8 przez bajt 65. Każdy znak, którego punkt kodowy jest większy niż 127 (0x7F) (znak inny niż ASCII) jest kodem od 2 do 4 bajty . Znak „€” (euro) jest zakodowany na przykład na 3 bajtach : 226, 130 i 172 (0xE2, 0x82 i 0xAC).
Numer (wartość skalarna) każdego punktu kodowego w Universal Character Set (UCS) jest podana przez normę ISO / IEC 10646, która przypisuje punkt kodowy do każdego ważnego znaku i umożliwia ich kodowanie poprzez przypisanie wartości skalarnej identycznej z punktem kodowym ; ten standard jest zawarty w standardzie Unicode (który używa tego samego katalogu od wersji 1.1).
Wszystkie " punkty kodowe " od U + 0000 do U + D7FF i od U + E000 do U + 10FFFF są reprezentowane w UTF-8 - nawet te przypisane do "nieznakowych" ( nieznakowych ) i wszystkie jeszcze nieprzypisane - i tylko te. Jedyne punkty kodowe ważne w przestrzeni UCS i które nie powinny być reprezentowane w UTF-8, to te przypisane do " pół- punktów kodowych " ( zamienniki w języku angielskim), ponieważ nie są one w pewien sposób reprezentowalne. bijective w kodowaniu UTF-16 i same w sobie nie są znakami: w przeciwieństwie do innych punktów kodowych, pół-kody nie mają zdefiniowanej " wartości skalarnej ".
Kod kropkowy o wartości skalarnej od 0 do 127 (punkty kodowe U + 0000 U + 007F, przypisane do znaków zbioru zakodowanych na 7 bitach w ASCII) są zakodowane na jednym bajcie, z którego bit najbardziej znaczący to zero.
Inne punkty kodowe (przypisane lub nie do znaków) mające wartość skalarną większą niż 127 (z wyjątkiem tych, którym przypisano „półkody”, które same nie są znakami) są kodowane na kilku bajtach, z których każdy ma swoje własne. najbardziej znaczący bit: najbardziej znaczące bity pierwszego bajtu zakodowanej sekwencji tworzą sekwencję o długości jedynek równą całkowitej liczbie bajtów (co najmniej 2) użytych dla całej sekwencji, po których następuje 0 i wymagane kolejne bajty ich dwa najbardziej znaczące bity ustawione na 10.
Zakodowane znaki | Reprezentacja binarna UTF-8 | Pierwszy prawidłowy bajt (szesnastkowo) | Znaczenie |
---|---|---|---|
U + 0000 do U + 007F | 0 xxxxxxx | 00 do 7F | 1 bajt, kodowanie 7 bitów |
U + 0080 do U + 07FF | 11 0 xxxxx 10 xxxxxx | C2 do DF | 2 bajty, kodowanie 11 bitów |
U + 0800 do U + FFFF | 111 0 X X X X 10 xxxxxx 10 xxxxxx | E0 do EF | 3 bajty, kodowanie 16 bitów |
U + 10000 do U + 10FFFF | 1111 0 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx | F0 do F4 | 4 bajty, kodowanie 21 bitów |
Ta zasada może zostać rozszerzona do ośmiu bajtów dla pojedynczego punktu kodowego (w celu reprezentowania punktów kodowych o długości do 42 bitów), ale obecna standardowa wersja UTF-8 ustala limit na cztery.
Kodowanie zabrania reprezentacji punktów kodowych zarezerwowanych dla półkodów (które nie mają zdefiniowanej wartości skalarnej, aby zachować zgodność z UTF-16, który również nie pozwala na ich reprezentację). Jednak zezwala na reprezentację punktów kodowych przypisanych do nieznaków (mimo że ich obecność jest zabroniona w tekście zgodnym).
Rodzaj | Postać |
Punkt kodowy (szesnastkowy) |
Wartość skalarna | Kodowanie UTF-8 | ||
---|---|---|---|---|---|---|
dziesiętny | dwójkowy | dwójkowy | szesnastkowy | |||
Kontrola | [NIE] | U + 0000 | 0 | 0 | 0 0000000 | 00 |
[NAS] | U + 001F | 31 | 1111 | 0 0011111 | 1F | |
Tekst | [SP] | U + 0020 | 32 | 100000 | 0 0100000 | 20 |
W | U + 0041 | 65 | 100 0001 | 0 100 0001 | 41 | |
~ | U + 007E | 126 | 111 1110 | 0 1111110 | 7E | |
Kontrola | [Z TEGO] | U + 007F | 127 | 111 1111 | 0 1111111 | 7F |
[PODKŁADKA] | U + 0080 | 128 | 000 1000 0000 | 110 000 10 10 000 000 | C2 80 | |
[APC] | U + 009F | 159 | 000 1001 1111 | 110 000 10 10 011 111 | C2 9F | |
Tekst | [NBSP] | U + 00A0 | 160 | 000 1010 0000 | 110 000 10 10 100 000 | C2 A0 |
mi | U + 00E9 | 233 | 000 1110 1001 | 110 000 11 10 101 001 | C3 A9 | |
߿ | U + 07FF | 2047 | 111 1111 1111 | 110 111 11 10 111 111 | DF BF | |
ࠀ | U + 0800 | 2048 | 1000 0000 0000 | 1110 0000 10 1000 00 10 000000 | E0 A0 80 | |
€ | U + 20AC | 8 364 | 100000 10101100 | 1110 0010 10 0000 10 10 101100 | E2 82 AC | |
| U + D7FF | 55 295 | 1101 0111 1111 1111 | 1110 1101 10 0111 11 10 111111 | ED 9F BF | |
Połowa kodetu | U + D800 | (nic) | (kodowanie zabronione) | |||
U + DFFF | ||||||
Użytek prywatny | [] | U + E000 | 57 344 | 1110 0000 0000 0000 | 1110 1110 10 0000 00 10 0000000 | EE 80 80 |
[] | U + F8FF | 63 743 | 1111 1000 1111 1111 | 1110 1111 10 1000 11 10 111111 | EF A3 BF | |
Tekst | U + F900 | 63 744 | 1111 1001 0000 0000 | 1110 1111 10 1001 00 10 000 000 | EF A4 80 | |
﷏ | U + FDCF | 64 975 | 1111 1101 1100 1111 | 1110 1111 10 1101 11 10 001111 | EF B7 8F | |
Nieznaki | U + FDD0 | 64 976 | 1111 1101 1101 0000 | 1110 1111 10 1101 11 10 010000 | EF B7 90 | |
U + FDEF | 65 007 | 1111 1101 1110 1111 | 1110 1111 10 1101 11 10 101111 | EF B7 AF | ||
Tekst | صلے | U + FDF0 | 65,008 | 1111 1101 1111 0000 | 1110 1111 10 1101 11 10 110000 | EF B7 B0 |
U + FFFD | 65 533 | 1111 1111 1111 1101 | 1110 1111 10 1111 11 10 111101 | EF BF BD | ||
Nieznaki | U + FFFE | 65 534 | 1111 1111 1111 1110 | 1110 1111 10 1111 11 10 111110 | EF BF BE | |
U + FFFF | 65 535 | 1111 1111 1111 1111 | 1110 1111 10 1111 11 10 111111 | EF BF BF | ||
Tekst | ? | U + 10 000 | 65 536 | 1 0000 0000 0000 0000 | 11 110 000 10 01 0000 10 0000 00 10 000000 | F0 90 80 80 |
? | U + 1D11E | 119 070 | 1 1101 0001 0001 1110 | 11 110 000 10 01 1101 10 0001 00 10 011 110 | F0 9D 84 9E | |
? | U + 1FFFD | 131,069 | 1 1111 1111 1111 1101 | 11110 000 10 01 1111 10 1111 11 10 111 101 | F0 9F BF BD | |
Nieznaki | U + 1FFFE | 131 070 | 1 1111 1111 1111 1110 | 11110 000 10 01 1111 10 1111 11 10 111 110 | F0 9F BF BE | |
U + 1FFFF | 131 071 | 1 1111 1111 1111 1111 | 11 110 000 10 01 1111 10 1111 11 10 111111 | F0 9F BF BF | ||
Tekst | ? | U + 20 000 | 131 072 | 10 0000 0000 0000 0000 | 11 110 000 10 10 0000 10 0000 00 10 000000 | F0 A0 80 80 |
? | U + 2FFFD | 196 605 | 10 1111 1111 1111 1101 | 11110 000 10 10 1111 10 1111 11 10 111 101 | F0 AF BF BD | |
Nieznaki | U + 2FFFE | 196 606 | 10 1111 1111 1111 1110 | 11110 000 10 10 1111 10 1111 11 10 111 110 | F0 AF BF BE | |
U + 2FFFF | 196.607 | 10 1111 1111 1111 1111 | 11 110 000 10 10 1111 10 1111 11 10 111111 | F0 AF BF BF | ||
... inne plany zastrzeżone ... | ||||||
Promocje | ? | U + E0000 | 917 504 | 1110 0000 0000 0000 0000 | 11110 011 10 10 0000 10 0000 00 10 000000 | F3 A0 80 80 |
? | U + EFFFD | 983,037 | 1110 1111 1111 1111 1101 | 11110 011 10 10 1111 10 1111 11 10 111101 | F3 AF BF BD | |
Nieznaki | U + EFFE | 983,038 | 1110 1111 1111 1111 1110 | 11110 011 10 10 1111 10 1111 11 10 111110 | F3 AF BF BE | |
U + EFF | 983,039 | 1110 1111 1111 1111 1111 | 11110 011 10 10 1111 10 1111 11 10 111111 | F3 AF BF BF | ||
Użytek prywatny | [?] | U + F0000 | 983,040 | 1111 0000 0000 0000 0000 | 11110 011 10 11 0000 10 0000 00 10 000000 | F3 B0 80 80 |
[ ] | U + FFFFD | 1 048 573 | 1111 1111 1111 1111 1101 | 11110 011 10 11 1111 10 1111 11 10 111101 | F3 | |
Nieznaki | U + FFFFE | 1 048 574 | 1111 1111 1111 1111 1110 | 11110 011 10 11 1111 10 1111 11 10 111110 | F3 BF BF BE | |
U + FFFFF | 1 048 575 | 1111 1111 1111 1111 1111 | 11110 011 10 11 1111 10 1111 11 10 111111 | F3 KP K K KP | ||
Użytek prywatny | [?] | U + 100 000 | 1 048 576 | 1 0000 0000 0000 0000 0000 | 11110 100 10 00 0000 10 0000 00 10 000000 | F4 80 80 80 |
[?] | U + 10FFFD | 1,114,109 | 1 0000 1111 1111 1111 1101 | 11110 100 10 00 1111 10 1111 11 10 111101 | F4 8F BF BD | |
Nieznaki | U + 10FFFE | 1,114,110 | 1 0000 1111 1111 1111 1110 | 11110 100 10 00 1111 10 1111 11 10 111110 | F4 8F BF BE | |
U + 10FFFF | 1,114,111 | 1 0000 1111 1111 1111 1111 | 11110 100 10 00 1111 10 1111 11 10 111111 | F4 8F BF BF |
W dowolnym ciągu znaków zakodowanym w UTF-8 zauważamy, że:
Największym punktem ważnego do przypisania kodu do ważnego znaku nie prywatny jest U + EFFFD w 15 -tego planu (nie jest jeszcze przypisany, ale może stać się w przyszłości), ale UTF-8 może być stosowany również w standardowy sposób, do reprezentowania dowolnego prawidłowego znaku do użytku prywatnego (w jednym z trzech zakresów U + E000 do U + F8FF, U + F0000 do U + FFFFD i U + 100000 do U + 10FFFD) .
To, czy akceptowane są znaki niebędące znakami lub znaki używane do użytku prywatnego, należy pozostawić aplikacjom lub protokołom transportu tekstu. Jednak znaki niebędące znakami nie są zwykle akceptowane w tekstach ściśle zgodnych ze standardem Unicode lub normą ISO/IEC 10646 .
Niektóre aplikacje nakładają dodatkowe ograniczenia na punkty kodowe, które mogą być używane (na przykład standardy HTML i XML zabraniają w każdym dokumencie zgodnym z tymi specyfikacjami obecności większości znaków kontrolnych między U + 0000 i U + 001F oraz między U + 0080 i U + 009F, poza kontrolką zakładki U + 0009 są uważane za puste znaki, a także zabraniają znaków niebędących znakami ).
Każdy punkt kodowy jest zawsze reprezentowany przez dokładnie tę samą sekwencję binarną, niezależnie od jej względnej pozycji w tekście, a sekwencje te są automatycznie synchronizowane na niepodzielnej pozycji znaczących punktów kodowych (tutaj bajty: zawsze możemy wiedzieć, czy bajt zaczyna się, czy nie efektywna sekwencja binarna); to kodowanie umożliwia zatem szybkie algorytmy wyszukiwania tekstu, takie jak algorytm Boyera-Moore'a .
Nie zawsze tak jest w przypadku kodowań kontekstowych (które generalnie wykorzystują kompresję danych , np. SCSU zdefiniowaną w opcjonalnej nocie technicznej standardu UTS nr 6 uzupełniającej standard Unicode) i które mogą wymagać przeczytania tekstu w całości od początku. na więcej niż jednej zmiennej stanu (lub zawierające dodatkowe kody redundancji); w najlepszym przypadku niektóre z tych kodowań mogą wymagać użycia złożonych algorytmów resynchronizacji, często opartych na heurystyce, które mogą zawieść lub prowadzić do fałszywych interpretacji, jeśli tekst nie zostanie odczytany od początku (np. BOCU -1).
Zasada i niepowtarzalność kodowaniaW powyższej tabeli widzimy, że znak „€” znajduje się w punkcie kodowym U + 20AC, w postaci dziesiętnej 8364 lub binarnej: 100 000 10101100.
Ta ostatnia liczba ma znaczące cyfry binarne, więc do zakodowania znaku „€” potrzeba co najmniej 14 bitów . Przedstawiony powyżej standard w rzeczywistości wymaga trzech bajtów do przedstawienia tych znaków.
Mając dostępne cztery bajty, możliwe byłoby umieszczenie zgodnie z tym standardem do 21 bitów , a więc w szczególności przedstawienie znaku „€” przez 00000 00 100000 10101100, dodając do niego 7 wiodących zer . Jednak standard narzuca, że program dekodujący UTF-8 nie może akceptować niepotrzebnie długich ciągów bajtów, jak w tym przykładzie, ze względów bezpieczeństwa (należy unikać zbyt tolerancyjnych testów podciągów). Zatem „€” będzie kodowane: 11100010 10000010 10101100, ale nie wolno używać kodu 11110000 10000010 10000010 10101100, wyprowadzonego z reprezentacji „€” na 21 bitach , chociaż jednoznaczne.
Taka forma dłuższa niż potrzeba nazywa się w języku angielskim overlong . Takie formularze (początkowo autoryzowane w starych specyfikacjach, zanim zostały kolejno ujednolicone przez początkowe RFC publikowane przez X/Open Consortium , następnie równolegle przez standard ISO 10646 i standard Unicode) są zabronione i muszą być traktowane jako nieważne.
Kodowanie jest predykcyjne i zawsze umożliwia znalezienie pozycji pierwszego bajtu ciągu reprezentującego punkt kodowy, z wartości dowolnego bajtu oraz z odczytu ograniczonej liczby sąsiednich bajtów, w dwóch kierunkach odczytu (to zawsze będzie samym bajtem lub pierwszym kwalifikującym się w jednym z 1 do 3 sąsiednich bajtów ).
Mówi się, że takie sekwencje są źle uformowane . (Patrz odniesienie powyżej, zwłaszcza druga tabela w klauzuli zgodności D36 normy lub artykuł Unicode ).
Z drugiej strony dozwolone są zastrzeżone punkty kodowe (jeszcze nie przydzielone do znaków) (nawet jeśli interpretacja znaków może pozostać niejednoznaczna): od aplikacji zależy, czy te znaki są dopuszczalne, czy nie, wiedząc, że te same aplikacje będą prawdopodobnie nadal używane, mimo że pozycje te zostały przypisane w standardach Unicode i ISO 10646 do nowych, w pełni poprawnych znaków.
Podobnie, inne punkty kodowe przypisane na stałe do innych „ nieznakowych ” są zabronione w tekstach zgodnych z ISO/IEC 10646 lub standardem Unicode : na przykład U + x FFFE do U + x FFFF (gdzie x oznacza szesnastkowy numer planu z 0 do 10). Pozostają jednak możliwe do zakodowania i dekodowania jako takie w UTF-8 ( nieznaki są dostępne dla aplikacji, które mogą z nich korzystać w ramach wewnętrznych interfejsów API, na przykład jako kody pośrednie niezbędne do realizacji niektórych procesów. ).
Ograniczenie przestrzeni reprezentacji tylko do punktów kodowych mniejszych lub równych U + 10FFFF (z wyłączeniem punktów kodowych przypisanych do punktów kodowych połówkowych ) nie zawsze było stosowane:
Tekst w US-ASCII jest kodowany identycznie w UTF-8 (gdy BOM nie jest używany).
Ponieważ znak jest podzielony na ciąg bajtów (nie słowa wielobajtowe), nie ma problemu endianness ( endianness English).
W przypadku większości łacińskich języków skryptowych, cyfrowych plików danych lub kodów źródłowych programów lub wielu tekstowych protokołów komunikacyjnych (takich jak FTP , HTTP lub MIME ), które używają znaków w dużym stopniu (lub czasami tylko częściowo) US-ASCII, UTF-8 wymaga mniej bajtów niż UTF-16 lub UTF-32 .
Wiele technik programowania komputerowego , które są poprawne z jednolitymi znakami jednobajtowymi, zachowuje ważność w UTF-8, w tym:
Jest to kodowanie samosynchronizujące (czytając pojedynczy bajt wiemy, czy jest to pierwszy znak, czy nie).
Punkty kodowe są reprezentowane w UTF-8 przez sekwencje bajtów o różnych rozmiarach (jak również w UTF-16), co komplikuje niektóre operacje na ciągach punktów kodowych: obliczanie liczby punktów kodowych; pozycjonowanie w określonej odległości (wyrażonej liczbą punktów kodowych) w pliku tekstowym i ogólnie każda operacja wymagająca dostępu do punktu kodowego pozycji N w łańcuchu.
Zmienna wielkość znaków ciągu uniemożliwia stosowanie wydajnych algorytmów w zakresie porównywania ciągów, takich jak algorytm Knutha-Morrisa-Pratta i przez to silnie obciąża masowe przetwarzanie danych, jak w przypadku eksploatacyjnych baz danych. Problem ten jest jednak bardziej związany z aspektami standaryzacji niż z kodowaniem.
W przypadku języków używających wielu znaków spoza US-ASCII UTF-8 zajmuje znacznie więcej miejsca. Na przykład popularne ideogramy używane w tekstach w językach azjatyckich, takich jak chiński lub japoński ( na przykład kanji ), używają 3 bajtów w UTF-8 w przeciwieństwie do 2 bajtów w UTF-16.
Ogólnie rzecz biorąc, pisma, które używają wielu punktów kodowych o wartości równej lub większej niż U + 0800, zajmują więcej pamięci niż gdyby były zakodowane w UTF-16 (UTF-32 będzie bardziej wydajny tylko w przypadku tekstów, które głównie używają pism. stare lub rzadkie kodowane poza podstawowym planem wielojęzycznym, to znaczy od U + 10000, ale może również okazać się przydatne lokalnie w niektórych procesach w celu uproszczenia algorytmów, ponieważ znaki tam zawsze mają stały rozmiar, konwertując wejście lub wyjście dane z lub do UTF-8 lub UTF-16 są trywialne).
Dzięki swojemu systemowi kodowania możliwe było przedstawienie kodu na różne sposoby w UTF-8, co mogło stanowić problem z bezpieczeństwem: źle napisany program może zaakceptować pewną liczbę reprezentacji UTF-8, zwykle nieważnych zgodnie z RFC 3629 oraz w specyfikacjach (obecnie równoważnych sobie) opublikowanych przez ISO 10646 i Unicode; ale tak nie było zgodnie z oryginalną specyfikacją, co pozwoliło na ich przekonwertowanie jako pojedynczy znak.
W ten sposób oprogramowanie wykrywające określone ciągi znaków ( na przykład w celu zapobiegania wstrzykiwaniu SQL ) może nie spełnić swojego zadania (nie ma to już miejsca, jeśli zostanie zweryfikowana zgodność kodowania ze ścisłą i ustandaryzowaną definicją UTF-8). wszystko).
Weźmy przykład z prawdziwego przypadku wirusa atakującego serwery HTTP w sieci Web w 2001 r. ( (en) Crypto-Gram: 15 lipca 2000 r. Luka w zabezpieczeniach Microsoft IIS i PWS rozszerzonego Unicode w zakresie przechodzenia katalogów Luka w zabezpieczeniach w Microsoft IIS 4.0 / 5.0 w przypadku przechodzenia katalogów w sieci Web ) . Wykrywaną sekwencją może być „/../” reprezentowana w ASCII ( a fortiori w UTF-8) przez bajty „ 2F 2E 2E 2F ” w notacji szesnastkowej . Jednak zniekształconym sposobem zakodowania tego ciągu w UTF-8 byłoby „ 2F C0 AE 2E 2F ”, nazywane również overlong form . Jeśli oprogramowanie nie jest starannie napisane, aby odrzucić ten łańcuch, na przykład poprzez umieszczenie go w formie kanonicznej , otwiera się potencjalne naruszenie bezpieczeństwa. Atak ten nazywa się przechodzeniem do katalogu .
Oprogramowanie akceptujące tekst zakodowany w UTF-8 zostało zabezpieczone przed systematycznym odrzucaniem tych długich form, ponieważ nie są one zgodne ze standardem: albo cały tekst jest odrzucany; ale czasami nieprawidłowe sekwencje są zastępowane znakiem podstawienia (zwykle U + FFFD, jeśli aplikacja akceptuje i przetwarza ten znak normalnie; czasami znak zapytania lub znak kontroli podstawienia SUB U + 001A z ASCII, co może stwarzać inne problemy z kompatybilnością); rzadziej te zabronione sekwencje są po cichu eliminowane (co jest bardzo mało zalecane).
UTF-8 może reprezentować znak kontrolny null (U + 0000) tylko jednym bajtem null, co stwarza problemy ze zgodnością z przetwarzaniem łańcuchów, które nie kodują oddzielnie ich efektywnej długości, ponieważ ten bajt null nie reprezentuje wtedy żadnego znaku, ale koniec ciągu (bardzo częsty przypadek w języku C lub C++ oraz w API systemów operacyjnych). Jeśli znak null musi być przechowywany w tekście w takich systemach, przed zakodowaniem w UTF-8 tak przekształconego tekstu konieczne będzie odwołanie się do systemu ucieczki, specyficznego dla tego języka lub systemu. W praktyce żaden poprawny tekst nie powinien zawierać tego znaku. Innym rozwiązaniem jest użycie jednej z sekwencji zabronionych w standardowym kodowaniu UTF-8 w celu zakodowania znaku przez tę sekwencję; ale tak zakodowany tekst nie będzie zgodny ze standardowym kodowaniem UTF-8, nawet jeśli zmodyfikowane w ten sposób kodowanie pozostaje zgodnym uniwersalnym formatem transformacji (który jednak nie powinien być oznaczony jako „UTF-8”). Zobacz sekcję poniżej dotyczącą niestandardowych wariantów opartych na UTF-8.
Używanie UTF8, podobnie jak każdego kodowania zmiennej pitch, w bazie danych stwarza wiele problemów z wydajnością.
Operacje porównania (=,>, <, BETWEEN, LIKE ...), sortowanie (ORDER BY), grupowanie (GROUP BY), takie jak operacje deduplikacji (DISTINCT) oparte na semantyce informacji, są niemożliwe do bezpośredniego zarządzania w UTF8 .
Rzeczywiście, w przypadku ciągów znaków zawierających tę samą liczbę liter (na przykład CHAR (8)), liczba bajtów może być różna (w szczególności ze względu na znaki diakrytyczne: akcenty, ligatury...), użyte algorytmy muszą dla w większości należy przeprowadzić wyrównanie, zanim będzie w stanie działać, co pociąga za sobą znaczne dodatkowe koszty przetwarzania.
Na przykład SZBD MySQL / MariaDB wybrał reprezentację znaków ciągów prezentowanych jako UTF8, systematycznie używając 3 bajtów na znak. Konsekwencje są następujące: potrojenie objętości danych i podzielenie przez trzy potencjalnej długości kluczy indeksowych w porównaniu z kodowaniem ASCII oraz wydłużenie czasów wykonywania porównań, sortowań, grupowań lub deduplikacji. Łańcuch jest ostatecznie zwracany w postaci UTF8 po oczyszczeniu zbędnych bajtów.
Inne DBMS, takie jak Microsoft SQL Server, zdecydowały się skompresować obsługę UTF8, wstawiając dodatkowe znaki w 2-bajtowym kodowaniu, opartym na UNICODE, wykorzystującym puste miejsca pozostawione przez specyfikację. Dodatkowy wysiłek związany z tłumaczeniem na UTF8 polega tylko na przekodowaniu znaków zakodowanych na 2 bajtach i rozwinięciu tych zakodowanych na 3.
UTF-8 została wynaleziona przez Kennetha Thompsona podczas kolacji z Rob Pike wokółwrzesień 1992. Nazywany wtedy FSS-UTF , został natychmiast użyty w systemie operacyjnym Plan 9 , nad którym pracowali. Jednym z ograniczeń, które należało rozwiązać, było zakodowanie znaków null i '/' jak w ASCII i że żaden bajt kodujący inny znak nie ma tego samego kodu. W ten sposób systemy operacyjne UNIX mogą kontynuować wyszukiwanie tych dwóch znaków w ciągu bez adaptacji oprogramowania.
FSS-UTF był przedmiotem wstępnego standardu X / Open 1993, który został zaproponowany ISO. Ten ostatni przyjął go jako część normy ISO/IEC 10646 pod nazwą najpierw UTF-2, a następnie UTF-8.
Wykres przedstawiający użycie UTF-8 (jasnoniebieski) wykraczającego poza inne główne kodowania znaków tekstowych w Internecie. Do 2010 r. rozpowszechnienie UTF-8 wynosiło około 50%, ale w 2016 r. było to więcej niż 90%. |
Statystyki odzwierciedlające technologie stosowane na stronach internetowych określone na podstawie technik rozpoznawania różnych wzorców, w tym elementów HTML, określonych tagów HTML (takich jak tag „generator meta”, kod JavaScript, kod CSS, struktura adresów URL witryny, linki poza witryną, nagłówki HTTP, na przykład pliki cookie, odpowiedzi HTTP na określone żądania, takie jak kompresja.
Statystyki oparte na próbce 10 milionów najlepszych stron internetowych według Alexy. Suma nie osiąga 100%, ponieważ niektóre serwery wykorzystują więcej niż jedną technologię. |
Źródło w3techs |
Oryginalne kodowanie FSS-UTF miało zastąpić wielobajtowe kodowanie UTF-1 pierwotnie zaproponowane przez ISO 10646. To początkowo permisywne kodowanie pozwalało na kilka reprezentacji binarnych tego samego znaku (jest to zabronione w standardowej wersji w RFC opublikowanej przez X / Otwarte Konsorcjum i zatwierdzone przez Kennetha Thompsona).
Ponadto może to (w wstępnej wersji nie zatrzymana) kodują wszystkie postacie, których kod punktu wartość składa się z 32 bitów , definiując ósmy typ bajtów (w sekwencjach zawierających do 6 bajtów ), na zamiast 7 typów z bajtów ostatecznie zachowanych do zakodowania (w sekwencjach zawierających również do 6 bajtów ) tylko kod wskazuje do 31 bitów w pierwotnej wersji UTF-8 (opublikowanej przez Konsorcjum X/Open pod nazwą FSS-UTF, następnie zaproponowanej przez komitet techniczny ISO 10646 jako propozycja „UTF-2”, a następnie nadal konkurująca z propozycją „UTF-1”, dopóki propozycja UTF-2 nie zostanie zachowana i przyjmie nazwę UTF-8 już zachowaną i używaną w X / Open i Plan 9).
Kodowanie UTF-8 dodatkowo ograniczona, gdy Unicode i ISO 10646 zgodził się przeznaczyć znaki tylko w pierwszych 17 płaszczyzn, w celu zapewnienia zgodności z UTF-16 nieokreślony (bez konieczności modyfikowania go) przez ograniczenie sekwencji dopóki „do 4 bajtów, tylko i używanie tylko pierwszych 5 z 7 typów bajtów (co wymagało zdefiniowania jako nieważnych nowych wartości bajtów i pewnych sekwencji bajtów, jednak ważnych indywidualnie).
IETF wymaga teraz UTF-8 jest obsługiwane domyślnie (a nie po prostu jako przedłużenie obsługiwane) przez wszystkich nowych protokołów komunikacyjnych z Internetem (opublikowanej w dokumencie RFC numerowane), który tekst wymiana (najstarsze protokoły, jednak nie zostały zmodyfikowane uczynić tę obsługę obowiązkową, ale tylko rozszerzoną, jeśli to możliwe, aby wspierać ją opcjonalnie, jeśli powoduje to niezgodności lub wprowadza nowe zagrożenia bezpieczeństwa: tak jest w przypadku protokołów internetowych powszechnie używanych jako DNS , HTTP , FTP , Telnet i HTML w swoich początkowych wersjach jeszcze nie standaryzowane przez W3C i ISO).
Stało się niezbędne, zwłaszcza w głównym oprogramowaniu do komunikacji internetowej i dzisiejszych systemach operacyjnych:
Jednak warianty UTF-8 (oparte na możliwościach kodowania początkowej nieograniczonej wersji) były nadal używane (zwłaszcza w implementacji serializacji ciągów Java), aby umożliwić kodowanie jako wielobajtowe uniknięcie pewnych zarezerwowanych znaków ASCII normalnie zakodowanych w pojedynczym bajt (na przykład znak null).
Dodatkowo niektóre systemy używają ciągów nieograniczonych: na przykład Java (i inne języki, w tym biblioteki manipulacji ciągami w C, PHP, Perl itp.) reprezentują znaki z jednostkami kodowania na 16 bitach (co umożliwia przechowywanie ciągów za pomocą UTF kodowanie -16, ale bez ograniczeń ważności nałożonych przez UTF-16 dotyczących zabronionych wartości i parowania w kolejności „pół-kodów” lub surogatów ); w tym przypadku jednostki kodujące są traktowane jako wartości binarne i konieczne jest ich indywidualne serializowanie (niezależnie od ich możliwej interpretacji jako znaki lub jako półpunkty kodu). W tym przypadku każda 16-bitowa jednostka kodująca, która reprezentuje „znak” (nieograniczony) jest serializowana w postaci sekwencji zawierających do 3 bajtów każda i niektórych bajtów zabronionych przez implementację (na przykład znaki null lub pasek ułamka ' /' w systemie plików lub inne jednobajtowe znaki w innych protokołach) są zakodowane jako dwubajtowe sekwencje specjalne, z których żadna nie jest zerowa, po prostu używając zasady kodowania pierwszej specyfikacji FSS-UTF (przed tą zachowaną przez X / Open Consortium w swoim początkowym RFC, gdzie te ucieczki były wyraźnie zabronione i tak pozostały).
Przed przyjęciem propozycji UTF-2 zachowanej dla UTF-8, istniał również wariant UTF-1, w którym wielokrotne kodowanie nie było możliwe, ale wymagało trudniejszego kodowania / dekodowania, aby uwzględnić pozycję każdego bajtu. szereg „magicznych” wartości.
Te warianty nie powinny być nazywane „UTF-8”.
Jeden z tych niestandardowych wariantów był jednak przedmiotem późniejszej standaryzacji (jako alternatywa dla UTF-16 i przy użyciu par „półkodów”, każdy zakodowany na 3 bajtach, czyli razem 6 bajtów zamiast 4). z UTF-8): patrz CESU-8 .
Przykład wariantu używanego w JavieNa przykład API integracji maszyn wirtualnych Java (dla JNI, Java Native Interface lub do serializacji prekompilowanych klas), które umożliwiają wymianę nieograniczonych łańcuchów Java w postaci sekwencji bajtów (w celu manipulowania nimi, używania lub tworzenia za pomocą kodu natywnego lub do przechowywania jako plik natywny zakodowany w ciągach bajtów), mają przyrostek „UTFChars” lub „UTF”, ale to kodowanie specyficzne dla Javy nie jest UTF-8 (dokumentacja firmy Sun odnosi się do niego jako zmodyfikowanego UTF , ale niektóre starsze dokumenty JNI nadal niepoprawnie odnoszą się do tego kodowania jako UTF-8 , co spowodowało pewne anomalie behawioralne niektórych natywnych bibliotek JNI, szczególnie w przypadku systemowych interfejsów API.starsze platformy natywne, które nie obsługują natywnie kodowania znaków powyżej 8 bitów ), ponieważ:
W konsekwencji :
Procesy te mogą być nieefektywne w przypadku łączenia dużych ilości tekstu, ponieważ wymagają przydzielenia dodatkowych buforów pamięci, aby następnie połączyć się w kodzie natywnym z interfejsami systemowymi lub sieciowymi, które akceptują tylko standardowe UTF-8.
Jednak JNI zapewnia również wydajniejszy binarny interfejs API pozwalający na bezpośrednie korzystanie z UTF-16, który jest w stanie łączyć się bezpośrednio z protokołami sieciowymi i interfejsami systemowymi (np. Windows API), które obsługują UTF-16, bez konieczności przydziału dodatkowej pamięci do transkodowania (tylko zgodność może być konieczne sprawdzenie, głównie w celu sprawdzenia zakodowanego tekstu pod kątem poprawnego sparowania półkodu lub surogatu , którym Java (podobnie jak inne języki programowania) pozwala manipulować bez ograniczeń ważności we własnych ciągach znaków nie przeznaczonych do przechowywania tylko tekstów zgodny z LUW). Ten binarny interfejs API jest obsługiwany na wszystkich systemach, na których przeniesiono Javę, nawet tych, których system operacyjny nie oferuje tekstowego interfejsu API Unicode (obsługę można wykonać w natywnej aplikacji hosta lub przy użyciu standardowych bibliotek dostarczonych z JVM lub innych niezależnych natywnych biblioteki.