Co to jest Unicode, UTF-8, UTF-16?


395

Jaka jest podstawa Unicode i dlaczego potrzeba UTF-8 lub UTF-16? Sprawdziłem to w Google i szukałem tutaj, ale nie jest to dla mnie jasne.

W VSS podczas porównywania plików czasami pojawia się komunikat informujący, że oba pliki mają różne UTF. Dlaczego miałoby tak być?

Proszę wyjaśnić w prosty sposób.


123
Wygląda na to, że musisz przeczytać Absolutne minimum Każdy programista Absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków ! To bardzo dobre wytłumaczenie tego, co się dzieje.
Brian Agnew


4
@John: to bardzo miłe wprowadzenie, ale nie jest to ostateczne źródło: pomija sporo szczegółów (co jest dobre dla przeglądu / wstępu!)
Joachim Sauer

5
Artykuł jest świetny, ale zawiera kilka błędów i reprezentuje UTF-8 w nieco konserwatywnym świetle. Sugeruję przeczytanie utf8everywhere.org jako suplementu.
Pavel Radzivilovsky

2
Spójrz na tę stronę internetową: utf8everywhere.org
Vertexwahn

Odpowiedzi:


550

Dlaczego potrzebujemy Unicode?

W (niezbyt) pierwszych dniach istniało tylko ASCII. To było w porządku, ponieważ wszystko, co kiedykolwiek będzie potrzebne, to kilka znaków kontrolnych, interpunkcja, cyfry i litery, takie jak te w tym zdaniu. Niestety, dzisiejszy dziwny świat globalnej komunikacji i mediów społecznościowych nie został przewidziany i nie jest niczym niezwykłym, aby zobaczyć angielski, العربية, 汉语, עִבְרִית, ελληνικά i ភាសាខ្មែរ w tym samym dokumencie (mam nadzieję, że nie złamałem żadnego starego przeglądarki).

Ale dla argumentu powiedzmy, że Joe Average jest programistą. Podkreśla, że ​​zawsze będzie potrzebował tylko angielskiego i jako taki chce tylko używać ASCII. Może to być w porządku dla użytkownika Joe , ale nie jest to w porządku dla Joe , twórcy oprogramowania . Około połowa świata używa znaków innych niż łacińskie, a używanie ASCII jest prawdopodobnie bez znaczenia dla tych ludzi, a ponadto zamyka swoje oprogramowanie na dużą i rozwijającą się gospodarkę.

Dlatego potrzebny jest zestaw znaków obejmujący wszystkie języki. Tak powstał Unicode. Przypisuje każdemu znakowi unikalny numer zwany punktem kodowym . Jedną zaletą Unicode w porównaniu z innymi możliwymi zestawami jest to, że pierwsze 256 punktów kodowych jest identycznych z ISO-8859-1 , a zatem także ASCII. Ponadto zdecydowana większość powszechnie używanych znaków jest reprezentowana tylko przez dwa bajty w regionie zwanym podstawową płaszczyzną wielojęzyczną (BMP) . Teraz potrzebne jest kodowanie znaków, aby uzyskać dostęp do tego zestawu znaków, a jak zadaje pytanie, skoncentruję się na UTF-8 i UTF-16.

Uwagi dotyczące pamięci

Ile bajtów daje dostęp do znaków w tych kodowaniach?

  • UTF-8:
    • 1 bajt: standardowy ASCII
    • 2 bajty: arabski, hebrajski, większość europejskich skryptów (w szczególności gruziński )
    • 3 bajty: BMP
    • 4 bajty: wszystkie znaki Unicode
  • UTF-16:
    • 2 bajty: BMP
    • 4 bajty: wszystkie znaki Unicode

Warto teraz wspomnieć, że do znaków spoza BMP należą starożytne skrypty, symbole matematyczne, symbole muzyczne oraz rzadsze znaki chińskie / japońskie / koreańskie (CJK) .

Jeśli będziesz pracował głównie ze znakami ASCII, to UTF-8 z pewnością jest bardziej wydajny pod względem pamięci. Jeśli jednak pracujesz głównie ze skryptami pozaeuropejskimi, użycie UTF-8 może być nawet 1,5 razy mniej wydajne niż UTF-16. W przypadku dużych ilości tekstu, takich jak duże strony internetowe lub długie dokumenty tekstowe, może to mieć wpływ na wydajność.

Podstawy kodowania

Uwaga: jeśli wiesz, jak są kodowane UTF-8 i UTF-16, przejdź do następnej sekcji, aby uzyskać praktyczne zastosowania.

  • UTF-8: W przypadku standardowych znaków ASCII (0-127) kody UTF-8 są identyczne. To sprawia, że ​​UTF-8 jest idealny, jeśli wymagana jest kompatybilność wsteczna z istniejącym tekstem ASCII. Inne znaki wymagają od 2-4 bajtów. Odbywa się to poprzez zarezerwowanie niektórych bitów w każdym z tych bajtów, aby wskazać, że jest to część znaku wielobajtowego. W szczególności pierwszym bitem każdego bajtu jest 1uniknięcie kolizji ze znakami ASCII.
  • UTF-16: W przypadku prawidłowych znaków BMP reprezentacja UTF-16 jest po prostu punktem kodowym. Jednak dla znaków spoza BMP UTF-16 wprowadza pary zastępcze . W takim przypadku połączenie dwóch dwubajtowych części mapuje na znak inny niż BMP. Te dwubajtowe części pochodzą z zakresu liczbowego BMP, ale są gwarantowane przez standard Unicode jako niepoprawne jako znaki BMP. Ponadto, ponieważ UTF-16 ma dwa bajty jako podstawową jednostkę, ma na to wpływ endianizm . Aby to zrekompensować, na początku strumienia danych można umieścić znak kolejności zarezerwowanych bajtów, który wskazuje na endianowość. Zatem jeśli czytasz dane wejściowe UTF-16 i nie określono endianizmu, musisz to sprawdzić.

Jak widać, UTF-8 i UTF-16 nie są ze sobą prawie kompatybilne. Więc jeśli wykonujesz operacje we / wy, upewnij się, że wiesz, jakiego kodowania używasz! Więcej informacji na temat tych kodowań można znaleźć w FAQ UTF .

Praktyczne uwagi dotyczące programowania

Typy znaków i ciągów znaków: Jak są one kodowane w języku programowania? Jeśli są to nieprzetworzone bajty, w momencie, gdy spróbujesz wyprowadzić znaki inne niż ASCII, możesz napotkać kilka problemów. Ponadto, nawet jeśli typ znaku jest oparty na UTF, nie oznacza to, że łańcuchy są poprawne UTF. Mogą zezwalać na sekwencje bajtów, które są nielegalne. Ogólnie rzecz biorąc, będziesz musiał użyć biblioteki obsługującej UTF, takiej jak ICU dla C, C ++ i Java. W każdym razie, jeśli chcesz wprowadzić / wyprowadzić coś innego niż domyślne kodowanie, najpierw musisz je przekonwertować.

Zalecane / domyślne / dominujące kodowanie: Gdy ma się wybór, którego UTF ma używać, zwykle najlepiej jest przestrzegać zalecanych standardów dla środowiska, w którym pracujesz. Na przykład UTF-8 dominuje w Internecie, a od HTML5 to jest zalecanym kodowaniem . I odwrotnie, zarówno środowiska .NET, jak i Java są oparte na typie znaków UTF-16. Myląco (i niepoprawnie) często pojawiają się odwołania do „kodowania Unicode”, które zwykle odnosi się do dominującego kodowania UTF w danym środowisku.

Obsługa bibliotek: używane biblioteki obsługują pewnego rodzaju kodowanie. Który? Czy obsługują skrzynki narożne? Ponieważ konieczność jest matką wynalazku, biblioteki UTF-8 będą na ogół poprawnie obsługiwały znaki 4-bajtowe, ponieważ 1, 2, a nawet 3 bajty mogą występować często. Jednak nie wszystkie rzekome biblioteki UTF-16 prawidłowo obsługują pary zastępcze, ponieważ występują one bardzo rzadko.

Liczenie znaków: Istnieje łączenie znaków w Unicode. Na przykład punkt kodowy U + 006E (n) i U + 0303 (łącząca tylda) tworzy ñ, ale punkt kodowy U + 00F1 tworzy ñ. Powinny wyglądać identycznie, ale prosty algorytm zliczania zwróci 2 dla pierwszego przykładu, 1 dla drugiego. To niekoniecznie jest złe, ale może nie być pożądanym rezultatem.

Porównywanie pod względem równości: A, А i Α wyglądają tak samo, ale są to odpowiednio: łaciński, cyrylica i grecki. Masz również przypadki takie jak C i Ⅽ, jedna to litera, a druga cyfra rzymska. Ponadto mamy również do rozważenia łączące postacie. Aby uzyskać więcej informacji, zobacz Zduplikowane znaki w Unicode .

Pary zastępcze: pojawiają się dość często na SO, więc podam tylko kilka przykładowych linków:

Inni ?:


11
Znakomita odpowiedź, duże szanse na nagrodę ;-) Osobiście dodam, że niektórzy argumentują za UTF-8 jako uniwersalnym kodowaniem znaków , ale wiem, że nie wszyscy są tego zdania.
Joachim Sauer

3
Na tym etapie wciąż dla mnie zbyt techniczna. Jak słowo hello jest przechowywane na komputerze w UTF-8 i UTF-16?
FirstName LastName

1
Czy mógłbyś szerzej wyjaśnić, dlaczego na przykład BMP zajmuje 3 bajty w UTF-8? Myślałem, że ponieważ jego maksymalna wartość to 0xFFFF (16 bitów), dostęp do niego zajmie tylko 2 bajty.
zaznacz

2
@mark Niektóre bity są zarezerwowane do celów kodowania. Dla punktu kodowego, który zajmuje 2 bajty w UTF-8, istnieje 5 zarezerwowanych bitów, pozostawiając tylko 11 bitów do wyboru punktu kodowego. U + 07FF kończy się jako najwyższy punkt kodowy reprezentowany w 2 bajtach.
DPenner1,

1
BTW - ASCII definiuje tylko 128 punktów kodowych, używając jedynie 7 bitów do przedstawienia. To ISO-8859-1 / ISO-8859-15, które definiują 256 punktów kodowych i używają 8 bitów do reprezentacji. Pierwsze 128 punktów kodowych we wszystkich 3 są takie same.
Tuxdude

67
  • Unicode
    • to zestaw znaków używanych na całym świecie
  • UTF-8
    • kodowanie znaków zdolne do kodowania wszystkich możliwych znaków (zwanych punktami kodowymi) w Unicode.
    • jednostka kodu to 8 bitów
    • użyj jednej do czterech jednostek kodu, aby zakodować Unicode
    • 00100100 dla „ $ ” (jeden 8 bitów); 11000010 10100010 dla „ ¢ ” (dwa 8-bitowe); 11100010 10000010 10101100 dla „ ” (trzy 8-bitowe)
  • UTF-16
    • inne kodowanie znaków
    • jednostka kodowa ma 16 bitów
    • użyj jednej lub dwóch jednostek kodu do zakodowania Unicode
    • 00000000 00100100 dla „ $ ” (jeden 16 bitów); 11011000 01010010 11011111 01100010 dla „ 𤭢 ” (dwa 16-bitowe)

1
Krótko i precyzyjnie
Aritra Chatterjee

30

Unicode jest dość złożonym standardem. Nie bój się zbytnio, ale bądź przygotowany na trochę pracy! [2]

Ponieważ zawsze potrzebny jest wiarygodny zasób, ale oficjalny raport jest ogromny, proponuję przeczytać:

  1. Absolutne minimum Każdy programista absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków (bez wymówek!) Wprowadzenie Joela Spolsky'ego, CEO Stack Exchange.
  2. Do BMP i nie tylko! Samouczek Erica Mullera, dyrektora technicznego, a następnie wiceprezesa, w Konsorcjum Unicode. (pierwsze 20 slajdów i gotowe)

Krótkie wyjaśnienie:

Komputery czytają bajty, a ludzie czytają znaki, dlatego używamy standardów kodowania do mapowania znaków na bajty. ASCII był pierwszym powszechnie stosowanym standardem, ale obejmuje tylko łacinę (7 bitów / znak może reprezentować 128 różnych znaków). Unicode jest standardem, którego celem jest objęcie wszystkich możliwych znaków na świecie (może pomieścić do 1 114 112 znaków, co oznacza maksymalnie 21 bitów / znak. Obecny Unicode 8.0 określa łącznie 120 737 znaków, i to wszystko).

Główna różnica polega na tym, że znak ASCII może pasować do bajtu (8 bitów), ale większość znaków Unicode nie. Tak więc stosuje się kodowanie formularzy / schematów (takich jak UTF-8 i UTF-16), a model znaków wygląda następująco:

Każdy znak zajmuje wyliczoną pozycję od 0 do 1 114 111 (hex: 0-10FFFF) zwaną punktem kodowym .
Formularz kodowania odwzorowuje punkt kodowy na sekwencję jednostek kodowych. Jednostka kodowa to sposób, w jaki znaki mają być zorganizowane w pamięci, jednostki 8-bitowe, jednostki 16-bitowe i tak dalej. UTF-8 wykorzystuje 1 do 4 jednostek 8 bitów, a UTF-16 wykorzystuje 1 lub 2 jednostki 16 bitów, aby pokryć cały Unicode o maksymalnej długości 21 bitów. Jednostki używają prefiksów, aby można było dostrzec granice znaków, a więcej jednostek oznacza więcej prefiksów, które zajmują bity. Tak więc, chociaż UTF-8 używa 1 bajtu dla skryptu łacińskiego, potrzebuje 3 bajtów dla późniejszych skryptów w Basic Multilingual Plane, podczas gdy UTF-16 używa 2 bajtów dla wszystkich tych. I to jest ich główna różnica.
Wreszcie schemat kodowania (jak UTF-16BE lub UTF-16LE) odwzorowuje (serializuje) sekwencję jednostek kodu na sekwencję bajtów.

znak: π
punkt kodowy:
formularze kodowania U + 03C0 (jednostki kodowe):
      UTF-8: CF 80
      Schematy
kodowania UTF-16: 03C0 (bajty):
      UTF-8: CF 80
      UTF-16BE: 03 C0
      UTF-16LE: C0 03

Wskazówka: cyfra szesnastkowa reprezentuje 4 bity, więc dwucyfrowa liczba szesnastkowa reprezentuje bajt
Spójrz również na mapy samolotów w Wikipedii, aby poznać układ zestawu znaków


19

Początkowo Unicode miał mieć 16-bitowe kodowanie o stałej szerokości (UCS-2). Pierwsi użytkownicy Unicode, tacy jak Java i Windows NT, budowali swoje biblioteki wokół 16-bitowych ciągów.

Później zakres Unicode został rozszerzony o znaki historyczne, które wymagałyby więcej niż 65 536 punktów kodowych obsługiwanych przez kodowanie 16-bitowe. Aby umożliwić reprezentowanie dodatkowych znaków na platformach, które korzystały z UCS-2, wprowadzono kodowanie UTF-16. Używa „par zastępczych” do reprezentowania postaci w dodatkowych płaszczyznach.

Tymczasem wiele starszych programów i protokołów sieciowych używało 8-bitowych ciągów. UTF-8 został stworzony, aby systemy te mogły obsługiwać Unicode bez konieczności używania szerokich znaków. Jest kompatybilny wstecz z 7-bitowym ASCII.


3
Warto zauważyć, że Microsoft nadal określa UTF-16 jako Unicode, co powoduje zamieszanie. Obie nie są takie same.
Mark Ransom,

15

W tym artykule wyjaśniono wszystkie szczegóły http://kunststube.net/encoding/

PISANIE DO BUFORA

jeśli napiszesz do 4-bajtowego bufora, symbolu z kodowaniem UTF8, twój plik binarny będzie wyglądał następująco:

00000000 11100011 10000001 10000010

jeśli napiszesz do 4-bajtowego bufora, symbolu z kodowaniem UTF16, twój plik binarny będzie wyglądał następująco:

00000000 00000000 00110000 01000010

Jak widać, w zależności od tego, jakiego języka użyjesz w swoich treściach, wpłynie to odpowiednio na twoją pamięć.

np. dla tego konkretnego symbolu: kodowanie UTF16 jest bardziej wydajne, ponieważ mamy 2 zapasowe bajty do wykorzystania dla następnego symbolu. Ale to nie znaczy, że musisz używać UTF16 dla japońskiego alfabetu.

CZYTANIE Z BUFORA

Teraz, jeśli chcesz przeczytać powyższe bajty, musisz wiedzieć, w jakim kodowaniu zostało zapisane, i poprawnie je odkodować.

np. jeśli zdekodujesz to: 00000000 11100011 10000001 10000010 do kodowania UTF16, skończysz na nie

Uwaga: Kodowanie i Unicode to dwie różne rzeczy. Unicode to duży (tabela) z każdym symbolem odwzorowanym na unikalny punkt kodowy. np. symbol (litera) ma (punkt kodowy) : 30 42 (szesnastkowy). Z drugiej strony kodowanie jest algorytmem, który konwertuje symbole na bardziej odpowiedni sposób, gdy są przechowywane na sprzęcie.

30 42 (hex) - > UTF8 encoding - > E3 81 82 (hex), which is above result in binary.

30 42 (hex) - > UTF16 encoding - > 30 42 (hex), which is above result in binary.

wprowadź opis zdjęcia tutaj


11

Unicode jest standardem, który odwzorowuje znaki we wszystkich językach na określoną wartość liczbową o nazwie Punkty kodowe . Powodem tego jest to, że umożliwia różne kodowania przy użyciu tego samego zestawu punktów kodowych.

UTF-8 i UTF-16 to dwa takie kodowania. Biorą punkty kodowe jako dane wejściowe i kodują je przy użyciu dobrze zdefiniowanej formuły w celu wytworzenia zakodowanego ciągu.

Wybór konkretnego kodowania zależy od twoich wymagań. Różne kodowania mają różne wymagania dotyczące pamięci i w zależności od znaków, z którymi będziesz mieć do czynienia, powinieneś wybrać kodowanie, które używa najmniejszej sekwencji bajtów do kodowania tych znaków.

Więcej szczegółowych informacji na temat Unicode, UTF-8 i UTF-16 można znaleźć w tym artykule,

Co każdy programista powinien wiedzieć o Unicode


9

Dlaczego Unicode? Ponieważ ASCII ma tylko 127 znaków. Te od 128 do 255 różnią się w różnych krajach, dlatego istnieją strony kodowe. Więc powiedzieli, że możemy mieć do 1114111 znaków. Jak więc przechowywać najwyższy punkt kodowy? Musisz go zapisać przy użyciu 21 bitów, więc użyjesz DWORDa zawierającego 32 bity i 11 bitów zmarnowanych. Jeśli więc używasz DWORD do przechowywania znaku Unicode, jest to najłatwiejszy sposób, ponieważ wartość w DWORD jest dokładnie zgodna z punktem kodowym. Ale tablice DWORD są oczywiście większe niż tablice WORD i oczywiście nawet większe niż tablice BYTE. Dlatego istnieje nie tylko utf-32, ale także utf-16. Ale utf-16 oznacza strumień WORD, a WORD ma 16 bitów, więc jak najwyższy punkt kodowy 1114111 pasuje do WORD? Nie może! Więc umieszczają wszystko powyżej 65535 w DWORD, który nazywają parą zastępczą. Taką parą zastępczą są dwa SŁOWA i można je wykryć, patrząc na pierwsze 6 bitów. A co z utf-8? Jest to tablica bajtów lub strumień bajtów, ale w jaki sposób najwyższy punkt kodowy 1114111 może się zmieścić w bajcie? Nie może! Okej, więc wstawili również DWORD, prawda? A może SŁOWO, prawda? Prawie dobrze! Wynaleźli sekwencje utf-8, co oznacza, że ​​każdy punkt kodowy wyższy niż 127 musi zostać zakodowany w sekwencji 2-bajtowej, 3-bajtowej lub 4-bajtowej. Łał! Ale jak możemy wykryć takie sekwencje? Cóż, wszystko do 127 to ASCII i jeden bajt. To, co zaczyna się od 110, to sekwencja dwubajtowa, to, co zaczyna się od 1110, to trzy bajtowa sekwencja, a to, co zaczyna się od 11110, to czterobajtowa sekwencja. Pozostałe bity tych tak zwanych „startbytes” należą do punktu kodowego. Teraz w zależności od sekwencji muszą następować następujące bajty. Kolejny bajt zaczyna się od 10, pozostałe bity to 6 bitów bitów danych i należą do punktu kodowego. Połącz bity ładunku startbyte i następujących bajtów / s, a otrzymasz kodowy punkt. To cała magia utf-8.


3
Przykład utf-8 znaku € (Euro) dekodowanego w 3-bajtowej sekwencji utf-8: E2 = 11100010 82 = 10000010 AC = 10101100 Jak widać, E2 zaczyna się od 1110, więc jest to sekwencja 3-bajtowa Jak widać , 82, a także AC zaczyna się od 10, więc są to następujące bajty Teraz łączymy „bity ładunku”: 0010 + 000010 + 101100 = 10000010101100, który jest dziesiętny 8364 Więc 8364 musi być współrzędną kodową znaku € (Euro).
wspaniały

5

ASCII - Oprogramowanie przydziela tylko 8 bajtów w pamięci dla danego znaku. Działa dobrze dla znaków angielskich i przyjętych (słowa pożyczkowe, takie jak fasada), ponieważ odpowiadające im wartości dziesiętne spadają poniżej 128 w wartości dziesiętnej. Przykładowy program C.

UTF-8 - Oprogramowanie przydziela 1 do 4 zmiennych 8-bitowych bajtów dla danego znaku. Co oznacza tutaj zmienna? Powiedzmy, że wysyłasz znak „A” przez strony HTML w przeglądarce (HTML to UTF-8), odpowiadająca mu wartość dziesiętna A wynosi 65, a po konwersji na dziesiętną staje się ona 01000010. Wymaga to tylko 1 bajtu , 1 bajt pamięci jest przydzielany nawet dla specjalnie przyjętych angielskich znaków, takich jak „ç” na fasadzie wyrazu. Jednak jeśli chcesz przechowywać znaki europejskie, wymaga 2 bajtów, więc potrzebujesz UTF-8. Jeśli jednak wybierasz znaki azjatyckie, potrzebujesz minimum 2 bajtów i maksymalnie 4 bajtów. Podobnie, emoji wymagają 3 do 4 bajtów. UTF-8 zaspokoi wszystkie Twoje potrzeby.

UTF-16 przydzieli minimum 2 bajty i maksymalnie 4 bajty na znak, nie przydzieli 1 lub 3 bajtów. Każdy znak jest reprezentowany w postaci 16 lub 32 bitów.

Dlaczego więc istnieje UTF-16? Początkowo Unicode był 16-bitowy, a nie 8-bitowy. Java przyjęła oryginalną wersję UTF-16.

W skrócie, nie potrzebujesz nigdzie UTF-16, chyba że został już przyjęty przez język lub platformę, nad którą pracujesz.

Program Java wywoływany przez przeglądarki internetowe używa UTF-16, ale przeglądarka internetowa wysyła znaki za pomocą UTF-8.


„Nigdzie nie potrzebujesz UTF-16, chyba że został już przyjęty przez język lub platformę”: To dobra uwaga, ale tutaj jest lista niewłączająca: JavaScript, Java, .NET, SQL NCHAR, SQL NVARCHAR , VB4, VB5, VB6, VBA, VBScript, NTFS, Windows API….
Tom Blodget,

2

UTF oznacza skrót od Unicode Transformation Format. Zasadniczo w dzisiejszym świecie istnieją skrypty napisane w setkach innych języków, formatach nieobjętych wcześniej używanym podstawowym ASCII. Stąd powstała UTF.

UTF-8 ma możliwości kodowania znaków, a jego jednostka kodowa to 8 bitów, natomiast w przypadku UTF-16 jest to 16 bitów.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.