Jaka jest maksymalna liczba bajtów dla znaku zakodowanego w UTF-8?

79

Jaka jest maksymalna liczba bajtów na jeden znak zakodowany w UTF-8?

Będę szyfrować bajty String zakodowanego w UTF-8 i dlatego muszę być w stanie obliczyć maksymalną liczbę bajtów dla String zakodowanego w UTF-8.

Czy ktoś mógłby potwierdzić maksymalną liczbę bajtów dla pojedynczego znaku zakodowanego w UTF-8?

Edd
źródło
1
Ty zrobił przyjrzeć wspólnych zasobów, takich jak Wikipedii UTF-8 artykułu , pierwszy ... prawda?
5
Przeczytałem kilka artykułów, które dały mieszane odpowiedzi ... Właściwie odniosłem wrażenie, że odpowiedź brzmiała 3, więc bardzo się cieszę, że zapytałem
Edd
2
Zostawię tutaj link do youtube ze znakami Toma Scotta, symbolami , cudem Unicode: goo.gl/sUr1Hf . Możesz usłyszeć i zobaczyć, jak wszystko ewoluuje, od kodowania znaków ASCII do utf-8.
Roy Lee
Zobacz także Obliczanie długości w UTF-8 w Java String bez faktycznego kodowania go dla przykładu kodu obliczającego długość
Vadzim

Odpowiedzi:

86

Maksymalna liczba bajtów na znak to 4 zgodnie z RFC3629, który ogranicza tablicę znaków do U+10FFFF:

W UTF-8 znaki z zakresu U + 0000..U + 10FFFF (dostępny zakres UTF-16) są kodowane przy użyciu sekwencji od 1 do 4 oktetów.

(Oryginalna specyfikacja dopuszczała maksymalnie sześciobajtowe kody znaków dla poprzednich punktów kodowych U+10FFFF).

Znaki z kodem mniejszym niż 128 będą wymagały tylko 1 bajtu, a następne kody 1920 znaków będą wymagały tylko 2 bajtów. Jeśli nie pracujesz z językiem ezoterycznym, pomnożenie liczby znaków przez 4 będzie znacznym przeszacowaniem.

Tamás
źródło
7
Czym jest dla Ciebie „język ezoteryczny”? Jakikolwiek język, który istniałby w świecie rzeczywistym, lub tekst, który przełącza się między różnymi językami świata? Czy programista funkcji UTF-8 na łańcuch powinien wybrać 2, 3 lub 4 jako mnożnik, jeśli dokonuje nadmiernej alokacji i zmniejsza wynik po rzeczywistej konwersji?
Daniel Marschall
1
@rinntech przez „język ezoteryczny” ma na myśli język, który ma wiele znaków Unicode o wysokiej wartości (coś z dołu tej listy: unicode-table.com/en/sections ). Jeśli musisz nadmiernie przydzielić, wybierz 4. Możesz wykonać podwójny przebieg, jeden, aby zobaczyć, ile bajtów będziesz potrzebować i zaalokować, a drugi, aby wykonać kodowanie; to może być lepsze niż przydzielanie ~ 4 razy więcej pamięci RAM.
matiu
9
Zawsze staraj się poradzić sobie z najgorszym przypadkiem: hacker9.com/single-message-can-crash-whatsapp.html
Evgen Bodunov,
20
Znaki CJKV zajmują głównie 3 bajty (niektóre rzadkie / archaiczne znaki zajmują 4 bajty), a nazywanie ich ezoterycznymi jest trochę naciągane (same Chiny to prawie 20% światowej populacji ...).
Tgr
3
Dlaczego było ograniczone do 4, skoro wcześniej było 6? Co powstrzymuje nas przed kontynuowaniem standardu i posiadaniem bajtu wiodącego 11111111i 2^(6*7)trochę miejsca na znaki?
Aaron Franke
32

Bez dalszego kontekstu powiedziałbym, że maksymalna liczba bajtów na znak w UTF-8 to

odpowiedź: 6 bajtów

Autor zaakceptowanej odpowiedzi poprawnie wskazał to jako „specyfikację oryginalną”. Było to ważne przez RFC-2279 1 . Jak zauważył J. Cocoe w komentarzach poniżej, zmieniło się to w 2003 r. Wraz z RFC-3629 2 , który ogranicza kodowanie UTF-8 do 21 bitów, które można obsługiwać za pomocą schematu kodowania wykorzystującego cztery bajty.

odpowiedz, jeśli obejmuje cały Unicode: 4 bajty

Ale w Javie <= v7 mówią o maksimum 3 bajtów do reprezentowania Unicode z UTF-8? Dzieje się tak, ponieważ oryginalna specyfikacja Unicode definiowała tylko podstawową płaszczyznę wielojęzyczną ( BMP ), tj. Jest to starsza wersja Unicode lub podzbiór współczesnego Unicode. Więc

odpowiedź, jeśli reprezentuje tylko oryginalny Unicode, BMP: 3 bajty

Ale OP mówi o pójściu w drugą stronę. Nie od znaków do bajtów UTF-8, ale od bajtów UTF-8 do „Ciągu” reprezentacji bajtów. Być może autor przyjętej odpowiedzi wyciągnął to z kontekstu pytania, ale niekoniecznie jest to oczywiste, więc może zdezorientować przypadkowego czytelnika tego pytania.

Przechodząc od kodowania UTF-8 do kodowania natywnego, musimy przyjrzeć się, jak zaimplementowano „ciąg znaków”. Niektóre języki, takie jak Python> = 3, będą reprezentować każdy znak za pomocą całkowitych punktów kodowych, co pozwala na 4 bajty na znak = 32 bity na pokrycie 21 potrzebnych nam dla Unicode, z pewnymi stratami. Dlaczego nie dokładnie 21 bitów? Ponieważ rzeczy są szybsze, gdy są wyrównane do bajtów. Niektóre języki, takie jak Python <= 2 i Java, reprezentują znaki przy użyciu kodowania UTF-16, co oznacza, że ​​muszą używać par zastępczych do reprezentowania rozszerzonego Unicode (nie BMP). Tak czy inaczej, to nadal maksymalnie 4 bajty.

odpowiedź, jeśli przechodzisz do UTF-8 -> kodowanie natywne: 4 bajty

Tak więc, ostateczny wniosek, 4 to najczęstsza prawidłowa odpowiedź, więc mamy rację. Ale przebieg może się różnić.

Joshua Richardson
źródło
5
„To jest nadal aktualna i poprawna specyfikacja, na Wikipedii” - już nie. Wkrótce po napisaniu tego (edycja z 2 kwietnia) artykuł Wikipedii dotyczący UTF-8 został zmieniony, aby wyjaśnić, że wersja 6-oktetowa nie jest częścią obecnej (2003) specyfikacji UTF-8.
J. Cocoe
„Ale w Javie <= v7 mówią o maksimum 3 bajtów do reprezentowania Unicode z UTF-8? To dlatego, że oryginalna specyfikacja Unicode definiowała tylko podstawową wielojęzyczną płaszczyznę” - To prawdopodobnie pierwotny powód, ale to nie jest cała historia. Java używa „zmodyfikowanego UTF-8”, a jedną z modyfikacji jest to, że „używa swojego własnego formatu dwukrotnego trzy-bajtowego” zamiast „czterobajtowego formatu standardowego UTF-8” (ich słów).
J. Cocoe
1
Nie ma punktów kodowych przydzielonych powyżej limitu 10FFFF (nieco ponad milion), a wiele implementacji UTF8 nigdy nie zaimplementowało sekwencji dłuższych niż 4 bajty (a niektóre tylko 3, np. MySQL), więc uważam, że można bezpiecznie ograniczyć do 4 bajtów na codepoint, nawet biorąc pod uwagę zgodność ze starszymi implementacjami. Musisz tylko upewnić się, że po drodze odrzuciłeś wszystko, co jest nieprawidłowe. Zauważ, że zalecenie matiu dotyczące alokacji po obliczeniu dokładnej długości bajtu jest dobre, jeśli to możliwe.
thomasrutter
2
„... [U] nicode może reprezentować do x10FFFF punktów kodowych. Więc włączając 0, oznacza to, że możemy to zrobić z tymi bajtami: F FF FF, czyli dwa i pół bajtu, czyli 20 bitów.” Uważam, że to trochę niepoprawne. Liczba punktów kodowych od 0x0 do 0x10FFFF wynosiłaby 0x110000, co mogłoby być reprezentowane w 1F FF FFlub 21 bitach. Liczba 0x110000 odpowiada 17 płaszczyznom punktów kodowych 0x10000 każda.
neuralmer
2
PSA: Wikipedia nie jest prawdziwym źródłem. Spójrz na rzeczywiste odniesienia do artykułu.
Nyerguds
0

Maksymalna liczba bajtów do obsługi US-ASCII, standardowego kodowania alfabetu angielskiego, to 1. Jednak ograniczanie tekstu do języka angielskiego staje się coraz mniej pożądane lub praktyczne w miarę upływu czasu.

Unicode został zaprojektowany, aby reprezentować glify wszystkich języków ludzkich, a także wiele rodzajów symboli z różnymi cechami renderowania. UTF-8 jest wydajnym kodowaniem dla Unicode, chociaż nadal jest nastawiony na angielski. UTF-8 jest samosynchronizujący: granice znaków można łatwo zidentyfikować poprzez skanowanie w poszukiwaniu dobrze zdefiniowanych wzorów bitowych w dowolnym kierunku.

Podczas gdy maksymalna liczba bajtów na znak UTF-8 wynosi 3 dla obsługi tylko 2-bajtowej przestrzeni adresowej Plane 0, Basic Multilingual Plane (BMP), która może być zaakceptowana jako minimalne wsparcie w niektórych aplikacjach, to 4 dla obsługi wszystkie 17 aktualnych płaszczyzn Unicode (stan na 2019). Należy zauważyć, że wiele popularnych znaków "emoji" prawdopodobnie znajduje się w Płaszczyźnie 16, która wymaga 4 bajtów.

Jednak dotyczy to tylko podstawowych glifów znaków. Istnieją również różne modyfikatory, takie jak umieszczanie akcentów na poprzednim znaku, a także możliwe jest połączenie dowolnej liczby punktów kodowych w celu zbudowania jednego złożonego „grafemu”. Dlatego w programowaniu w świecie rzeczywistym użycie lub założenie stałej maksymalnej liczby bajtów na znak prawdopodobnie ostatecznie doprowadzi do problemu z aplikacją.

Z tych rozważań wynika, że ​​ciągi znaków UTF-8 nie powinny być „rozszerzane” do tablic o stałej długości przed przetwarzaniem, jak to czasami robiono. Zamiast tego programowanie powinno odbywać się bezpośrednio, przy użyciu funkcji napisów zaprojektowanych specjalnie dla UTF-8.

David Spector
źródło
Uwaga: akapit o niestosowaniu tablicy znaków o stałej szerokości to moja własna opinia. Jestem gotów edytować tę odpowiedź w odpowiedzi na komentarze.
David Spector