Jak działa „kodowanie o zmiennej szerokości” UTF-8?

110

Standard Unicode ma wystarczającą liczbę punktów kodowych, że potrzebujesz 4 bajtów, aby je wszystkie zapisać. To właśnie robi kodowanie UTF-32. Jednak kodowanie UTF-8 w jakiś sposób wciska je do znacznie mniejszych przestrzeni, używając czegoś, co nazywa się „kodowaniem o zmiennej szerokości”.

W rzeczywistości udaje mu się przedstawić pierwsze 127 znaków US-ASCII w jednym bajcie, który wygląda dokładnie jak prawdziwy ASCII, więc możesz interpretować wiele tekstu ascii tak, jakby był to UTF-8, nie robiąc nic z tym. Sztuczka. Jak to działa?

Mam zamiar zadać i odpowiedzieć tutaj na moje własne pytanie, ponieważ właśnie trochę przeczytałem, aby to zrozumieć i pomyślałem, że może to zaoszczędzić komuś trochę czasu. Poza tym może ktoś może mnie poprawić, jeśli coś źle zrozumiałem.

dsimard
źródło
8
Straight Unicode nie wymaga 32 bitów do zakodowania wszystkich punktów kodowych. Kiedyś twierdzili, że istnieje wiele możliwych punktów kodowych, ale po wystartowaniu UTF-8 celowo ograniczyli się do 21 bitów, tak aby UTF-8 nigdy nie przekroczył 4 bajtów na znak. Unicode obecnie wymaga tylko 17 bitów do przechowywania wszystkich możliwych punktów kodowych. Bez tego ograniczenia UTF-8 mógłby osiągnąć 6 bajtów na znak.
Warren Young
@Warren: w większości dokładne, ale Unicode to kod 21-bitowy (od U ​​+ 0000 do U + 10FFFF).
Jonathan Leffler
2
@Warren: UTF-8 z ograniczeniem do 4 bajtów mógł obsługiwać do U + 1FFFFF. Ze względu na UTF-16 wprowadzono ograniczenie do U + 10FFFF.
dan04
@ dan04 Czy mamy jakieś proste wyjaśnienie, w jaki sposób jest on ograniczony do U + 10FFFF przez UTF-16? Byłoby miło dowiedzieć się więcej na ten temat.
A-letubby
@ A-letubby: Ponieważ „zastępcze” kody UTF-16 są przydzielane w taki sposób, że istnieje 1024 surogatów ołowiu i 1024 surogatów śladu (i można ich używać tylko parami), aby uzyskać 2 ^ 20 (około miliona) dodatkowych znaków dostępne poza BMP. Dodane do 2 ^ 16 znaków dostępnych w BMP, daje to 0x110000 możliwych znaków.
dan04

Odpowiedzi:

129

Każdy bajt zaczyna się od kilku bitów, które informują, czy jest to jednobajtowy punkt kodowy, wielobajtowy punkt kodowy, czy też kontynuacja wielobajtowego punktu kodowego. Lubię to:

0xxx xxxx    A single-byte US-ASCII code (from the first 127 characters)

Każdy z wielobajtowych punktów kodowych zaczyna się od kilku bitów, które zasadniczo mówią „hej, musisz także przeczytać następny bajt (lub dwa lub trzy), aby dowiedzieć się, kim jestem”. Oni są:

110x xxxx    One more byte follows
1110 xxxx    Two more bytes follow
1111 0xxx    Three more bytes follow

Wreszcie wszystkie bajty następujące po tych kodach startowych wyglądają następująco:

10xx xxxx    A continuation of one of the multi-byte characters

Ponieważ możesz określić, na jaki bajt patrzysz, z kilku pierwszych bitów, to nawet jeśli coś zostanie gdzieś zniekształcone, nie stracisz całej sekwencji.

dsimard
źródło
14
W tej historii jest coś więcej - ponieważ kodowanie musi być możliwie najkrótszym kodowaniem znaku, co oznacza, że ​​na przykład bajty 0xC0 i 0xC1 nie mogą występować w UTF-8; i w rzeczywistości nie może też 0xF5..0xFF. Zobacz często zadawane pytania dotyczące UTF-8 na stronie unicode.org/faq/utf_bom.html lub unicode.org/versions/Unicode5.2.0/ch03.pdf
Jonathan Leffler
2
Dlaczego nie mógł użyć jednego znaku do powiedzenia next char is continuation? Gdybyśmy otrzymali znak 3-bajtowy, wyglądałoby to 1xxxxxxx 1xxxxxxx 0xxxxxxxtak:, więc mniej miejsca byłoby marnowane.
9
@Soaku sprawia, że ​​UTF-8 jest tak zwanym kodem „samosynchronizującym”. Oznacza to, że jeśli z powodu błędów brakuje części sekwencji, można to wykryć i odrzucić to, co zostało zniekształcone. Jeśli odczytasz bajt zaczynający się od 10xx i nie ma poprzedzającego bajtu „startowego”, możesz go odrzucić, ponieważ jest bez znaczenia. Jeśli masz system, jak opisałeś, i jeden z pierwszych bajtów zostanie utracony, możesz skończyć z innym, prawidłowym znakiem bez wskazania jakiegokolwiek błędu. Ułatwi to również zlokalizowanie następnego prawidłowego znaku, a także poprawi brakujące bajty „kontynuacji”.
htmlcoderexe
9

RFC3629 - UTF-8, format transformacji ISO 10646 jest tutaj ostatecznym autorytetem i zawiera wszystkie wyjaśnienia.

Krótko mówiąc, kilka bitów w każdym bajcie zakodowanej w UTF-8 sekwencji od 1 do 4 bajtów reprezentującej pojedynczy znak jest używanych do wskazania, czy jest to bajt końcowy, bajt wiodący, a jeśli tak, to ile kolejnych bajtów. Pozostałe bity zawierają ładunek.

azheglov
źródło
1
Ummmm, głupi ja, myślałem, że standard Unicode był ostatecznym autorytetem w UTF-8
John Machin
6
Standard Unicode definiuje sam Unicode. Nie definiuje różnych metod, dzisiejszych i przyszłych, które mogą być używane do kodowania tekstów Unicode do różnych celów (takich jak przechowywanie i transport). UTF-8 jest jedną z tych metod, a powyższe odniesienie dotyczy dokumentu, który ją definiuje.
azheglov
1
RFC3629, strona 3, sekcja 3. mówi, że „UTF-8 jest zdefiniowany w standardzie Unicode”.
John Machin
Poszukiwanie linków na unicode.org zaprowadziło mnie do sekcji 3.9 standardu Unicode, a konkretnie do definicji D92 (a także stycznie D86). Nie mam pojęcia, w jakim stopniu ten link będzie przydatny, gdy zostaną wydane nowe wersje, ale wyobrażam sobie, że chcą utrzymać stabilne identyfikatory sekcji i definicji we wszystkich wersjach.
tripleee
4

UTF-8 był kolejnym systemem do przechowywania ciągu punktów kodowych Unicode, tych magicznych liczb U +, w pamięci przy użyciu 8-bitowych bajtów. W UTF-8 każdy punkt kodowy od 0-127 jest przechowywany w jednym bajcie. Tylko punkty kodowe 128 i wyższe są przechowywane przy użyciu 2, 3, w rzeczywistości do 6 bajtów.

Fragment z absolutnego minimum Każdy programista absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków (bez wymówek!)

Andrzej
źródło
To dobry artykuł, ale wydaje się, że Joel nie ma racji co do maksymalnej długości sekwencji; strona Wikipedii pokazuje tylko 1..4 bajtów na znak.
zrelaksuj się
4
Jak powiedziałem powyżej, kiedy po raz pierwszy utworzono UTF-8, Unicode rościł sobie prawo do 32-bitów dla punktów kodowych, nie dlatego, że naprawdę tego potrzebowali, tylko dlatego, że 32-bity to wygodna wartość i już przepadli poprzedni limit znaków 16-bitowych. Po tym, jak UTF-8 okazał się popularny, zdecydowali się na zawsze ograniczyć maksymalną liczbę punktów kodowych do 2 ^ 21, co jest największą wartością, którą można zakodować za pomocą 4 bajtów schematu UTF-8. W Unicode jest nadal mniej niż 2 ^ 17 znaków, więc możemy ponad czterokrotnie zwiększyć liczbę znaków w Unicode dzięki temu nowemu schematowi.
Warren Young
Ok, ale nie wyjaśnienie zadane przez OP.
Nishant
2
To nie jest odpowiedź na pytanie.
Koray Tugay