Różnica między UTF-8 i UTF-16?

137

Różnica między UTF-8 i UTF-16? Dlaczego tego potrzebujemy?

MessageDigest md = MessageDigest.getInstance("SHA-256");
String text = "This is some text";

md.update(text.getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] digest = md.digest();
theJava
źródło
2
jon skeet ma dobry artykuł o kodowaniu ... csharpindepth.com/Articles/General/Unicode.aspx
Mitch Wheat,

Odpowiedzi:

284

Wydaje mi się, że w Internecie jest wiele dobrych artykułów na ten temat, ale oto krótkie podsumowanie.

Zarówno UTF-8, jak i UTF-16 to kodowania o zmiennej długości. Jednak w UTF-8 znak może zajmować minimum 8 bitów, podczas gdy w UTF-16 długość znaku zaczyna się od 16 bitów.

Główne zalety UTF-8:

  • Podstawowe znaki ASCII, takie jak cyfry, znaki łacińskie bez akcentów itp., Zajmują jeden bajt, co jest identyczne z reprezentacją US-ASCII. W ten sposób wszystkie łańcuchy US-ASCII stają się poprawnymi kodami UTF-8, co w wielu przypadkach zapewnia przyzwoitą kompatybilność wsteczną.
  • Brak bajtów zerowych, co pozwala na użycie ciągów zakończonych znakiem null, wprowadza to również dużą część kompatybilności wstecznej.
  • UTF-8 jest niezależny od kolejności bajtów, więc nie musisz martwić się problemem Big Endian / Little Endian.

Główne wady UTF-8:

  • Wiele typowych znaków ma różną długość, co strasznie spowalnia indeksowanie według punktu kodowego i obliczanie liczby punktów kodowych.
  • Mimo że kolejność bajtów nie ma znaczenia, czasami UTF-8 nadal ma BOM (znak kolejności bajtów), który służy do powiadomienia, że ​​tekst jest zakodowany w UTF-8, a także łamie zgodność z oprogramowaniem ASCII, nawet jeśli tekst zawiera tylko znaki ASCII . Oprogramowanie firmy Microsoft (takie jak Notatnik) szczególnie lubi dodawać BOM do UTF-8.

Główne zalety UTF-16:

  • Znaki BMP (podstawowa wielojęzyczna płaszczyzna), w tym łaciński, cyrylica, większość chińskich (w ChRL obsługa niektórych punktów kodowych poza BMP jest obowiązkowa), większość japońskiego może być reprezentowana przez 2 bajty. Przyspiesza to indeksowanie i obliczanie liczby punktów kodowych w przypadku, gdy tekst nie zawiera dodatkowych znaków.
  • Nawet jeśli tekst zawiera znaki uzupełniające, nadal są one reprezentowane przez pary wartości 16-bitowych, co oznacza, że ​​całkowita długość jest nadal podzielna przez dwa i pozwala na użycie 16-bitowego charjako pierwotnego składnika ciągu.

Główne wady UTF-16:

  • Wiele bajtów zerowych w łańcuchach US-ASCII, co oznacza brak ciągów zakończonych znakiem null i dużo zmarnowanej pamięci.
  • Używanie go jako kodowania o stałej długości „działa” głównie w wielu typowych scenariuszach (szczególnie w USA / UE / krajach z alfabetem cyrylicy / Izrael / kraje arabskie / Iran i wiele innych), często prowadząc do zerwania wsparcia tam, gdzie tak nie jest. Oznacza to, że programiści muszą być świadomi par zastępczych i odpowiednio je obsługiwać w przypadkach, w których ma to znaczenie!
  • Ma zmienną długość, więc liczenie lub indeksowanie punktów kodowych jest kosztowne, chociaż mniej niż UTF-8.

Ogólnie rzecz biorąc, UTF-16 jest zwykle lepszy do reprezentacji w pamięci, ponieważ BE / LE nie ma tam znaczenia (po prostu użyj kolejności natywnej), a indeksowanie jest szybsze (tylko nie zapomnij o prawidłowej obsłudze par zastępczych). Z drugiej strony UTF-8 jest wyjątkowo dobry w przypadku plików tekstowych i protokołów sieciowych, ponieważ nie ma problemu z BE / LE i często przydaje się zakończenie zerowe, a także kompatybilność z ASCII.

Siergiej Tachenov
źródło
3
Brakuje tylko części BE / LE na UTF16 :) UTF-8 ma jeszcze jedną wadę, może generować dłuższe wyjście niż UTF16
bestsss
4
Tak, zapomniałem o BE / LE. Nie jest to jednak wielka sprawa, szczególnie w przypadku używania w pamięci. UTF-8 generuje dłuższe dane wyjściowe tylko wtedy, gdy używane są znaki trzy-bajtowe, ale oznacza to głównie chiński i japoński. Z drugiej strony, jeśli tekst zawiera dużo znaków US-ASCII, może generować krótszy wynik, więc to, czy jest to minus, czy nie, zależy od konkretnej sytuacji.
Siergiej Tachenov
Nawet nie pomyślałem o natychmiastowym pro utf-8, krótszej długości. W przybliżeniu dłuższe wyjście utf-8 było „może” z jakiegoś powodu, ale jeśli cel znajduje się na dalekim wschodzie, domyślne kodowanie powinno być utf-16. Jak na przykład md.update (text.getBytes ("UTF-8")); kodowanie nie ma znaczenia, ponieważ hash jest stabilny w obie strony.
bestsss
Najszybszym sposobem konwersji ciągu znaków na tablicę bajtów jest coś takiego, opublikowane jako przykład
bestsss
Mówisz, że znaki mają różną długość w UTF-8, więc spowalnia indeksowanie i obliczanie długości, ale wątpię, czy znaki w UTF-16 mają również inną długość, czy indeksowanie i obliczanie długości UTF-16 powinno być szybsze?
nicky_zs
19

Są to po prostu różne schematy reprezentowania znaków Unicode.

Oba mają zmienną długość - UTF-16 wykorzystuje 2 bajty na wszystkie znaki w podstawowej płaszczyźnie wielojęzycznej (BMP), która zawiera większość powszechnie używanych znaków.

UTF-8 wykorzystuje od 1 do 3 bajtów dla znaków w BMP, do 4 dla znaków w obecnym zakresie Unicode od U + 0000 do U + 1FFFFF i można go rozszerzyć do U + 7FFFFFFF, jeśli zajdzie taka potrzeba ... ale przede wszystkim wszystkie znaki ASCII są reprezentowane w jednym bajcie.

Na potrzeby podsumowania wiadomości nie ma znaczenia, który z nich wybierzesz, o ile każdy, kto próbuje odtworzyć podsumowanie, korzysta z tej samej opcji.

Zobacz tę stronę, aby uzyskać więcej informacji na temat UTF-8 i Unicode.

(Zauważ, że wszystkie znaki Java są punktami kodowymi UTF-16 w BMP; aby przedstawić znaki powyżej U + FFFF, musisz użyć par zastępczych w Javie.)

Jon Skeet
źródło
5

Bezpieczeństwo: używaj tylko UTF-8

Różnica między UTF-8 i UTF-16? Dlaczego tego potrzebujemy?

W implementacjach UTF-16 było co najmniej kilka luk w zabezpieczeniach . Szczegółowe informacje można znaleźć w Wikipedii .

WHATWG i W3Cteraz ogłosił , że tylko UTF-8 ma być używany w sieci.

Zarysowane tutaj problemy [bezpieczeństwa] znikają, gdy używa się wyłącznie UTF-8, co jest jednym z wielu powodów, dla których jest teraz obowiązkowe kodowanie wszystkich rzeczy.

Inne grupy mówią to samo.

Tak więc, podczas gdy UTF-16 może nadal być używany wewnętrznie przez niektóre systemy, takie jak Java i Windows, to niewielkie wykorzystanie UTF-16, które mogłeś widzieć w przeszłości do plików danych, wymiany danych itp., Prawdopodobnie zniknie całkowicie.

Basil Bourque
źródło
4

Nie ma to związku z UTF-8/16 (generalnie, chociaż konwertuje do UTF16, a część BE / LE można ustawić w jednej linii), ale poniżej znajduje się najszybszy sposób konwersji String na bajt []. Na przykład: dobre dokładnie dla podanego przypadku (kod skrótu). String.getBytes (enc) jest stosunkowo powolny.

static byte[] toBytes(String s){
        byte[] b=new byte[s.length()*2];
        ByteBuffer.wrap(b).asCharBuffer().put(s);
        return b;
    }
bestsss
źródło
-2

Prostym sposobem na rozróżnienie UTF-8 i UTF-16 jest zidentyfikowanie podobieństw między nimi.

Poza tym, że mają ten sam numer Unicode dla danego znaku, każdy ma swój własny format.

Venkateswara Rao
źródło