Dieser Artikel zielt darauf ab, die Prinzipien und Mechanismen der Arbeit von Textcodierungen zusammenzuführen und zu zerlegen, und diesen Mechanismus im Detail zu zerlegen und zu erklären. Es ist nützlich für diejenigen, die sich nur grob vorstellen können, was Textcodierungen sind und wie sie funktionieren, wie sie sich voneinander unterscheiden, warum manchmal unlesbare Zeichen erscheinen, welches Codierungsprinzip verschiedene Codierungen haben.
Um ein detailliertes Verständnis für dieses Problem zu erhalten, müssen Sie mehr als einen Artikel lesen und zusammenstellen und viel Zeit damit verbringen. In diesem Material ist dies alles zusammengefasst und sollte theoretisch Zeit sparen, und die Analyse hat sich meiner Meinung nach als ziemlich detailliert herausgestellt.
Was wird unter der Kürzung passieren: das Funktionsprinzip von Einzelbyte-Codierungen (ASCII, Windows-1251 usw.), die Voraussetzungen für das Erscheinungsbild von Unicode, was ist Unicode, Unicode-Codierungen UTF-8, UTF-16, ihre Unterschiede, grundlegende Merkmale, Kompatibilität und Inkompatibilität verschiedener Codierungen, Prinzipien der Zeichencodierung, praktische Analyse der Codierung und Decodierung.
Das Thema Kodierung hat jetzt sicherlich an Relevanz verloren, aber ich denke, es wird nicht überflüssig sein zu wissen, wie sie jetzt funktionieren und wie sie vorher gearbeitet haben.
Unicode-Voraussetzungen
Ich denke, es lohnt sich, von einer Zeit an zu beginnen, in der die Computerisierung noch nicht so weit fortgeschritten war und nur an Dynamik gewann. Damals glaubten die Entwickler und Standardisierer nicht, dass Computer und das Internet eine solch enorme Popularität und Verbreitung erlangen würden. Eigentlich entstand dann das Bedürfnis, den Text zu verschlüsseln. In welcher Form war es notwendig, die Buchstaben im Computer zu speichern, und er (der Computer) versteht nur Einsen und Nullen. Aus diesem Grund wurde eine Ein-Byte-ASCII-Codierung entwickelt (höchstwahrscheinlich ist dies nicht die erste Codierung, aber die häufigste und indikativste, weshalb wir sie als Referenz betrachten werden). Wie ist sie? Jedes Zeichen in dieser Codierung wird mit 8 Bits codiert. Es ist leicht zu berechnen, dass die Codierung basierend darauf 256 Zeichen enthalten kann (acht Bits, Nullen oder Einsen 2
8 = 256).
Die ersten 7 Bits (128 Zeichen, 2,
7 = 128) in dieser Codierung wurden lateinischen Zeichen, Steuerzeichen (wie Zeilenumbrüche, Tabulatoren usw.) und Grammatikzeichen zugewiesen. Der Rest war den Landessprachen vorbehalten. Das heißt, es stellte sich heraus, dass die ersten 128 Zeichen immer gleich sind. Wenn Sie Ihre Muttersprache codieren möchten, verwenden Sie bitte die verbleibende Kapazität. Tatsächlich erschien ein riesiger Zoo nationaler Kodierungen. Und jetzt können Sie sich vorstellen, wenn ich zum Beispiel in Russland bin und ein Textdokument erstelle, wird es standardmäßig in Windows-1251-Codierung (in Windows verwendete russische Codierung) erstellt und an jemanden gesendet, zum Beispiel in den USA. Sogar die Tatsache, dass mein Gesprächspartner Russisch kann, hilft ihm nicht, denn wenn er mein Dokument auf seinem Computer öffnet (im Editor mit der Standardcodierung desselben ASCII), sieht er keine russischen Buchstaben, sondern krakozyabry. Genauer gesagt werden die Stellen in dem Dokument, die ich in Englisch schreibe, ohne Probleme angezeigt, da die ersten 128 Zeichen der Windows-1251- und ASCII-Codierung gleich sind, aber wo ich den russischen Text geschrieben habe, wenn er nicht die richtige Codierung in seinem Editor angibt, in Form eines Krokodils.
Ich denke, das Problem mit den nationalen Kodierungen ist verständlich. Tatsächlich gibt es viele dieser nationalen Kodierungen, und das Internet ist sehr weit verbreitet, und jeder wollte in seiner eigenen Sprache schreiben und wollte nicht, dass seine Sprache wie krummes Haar aussieht. Es gab zwei Möglichkeiten, für jede Seite der Codierung einen gemeinsamen Wert für alle Zeichen in der Weltsymboltabelle anzugeben. Die zweite Option hat gewonnen, also haben wir eine Unicode-Zeichentabelle erstellt.
ASCII kleine Werkstatt
Es mag elementar erscheinen, aber da ich mich entschlossen habe, alles im Detail zu erklären, ist dies notwendig.
Hier ist die ASCII-Zeichentabelle:

Hier haben wir 3 Spalten:
- Dezimalzahl
- Zeichennummer im hexadezimalen Format
- Darstellung des Symbols selbst.
Kodieren Sie also die Zeichenfolge "ok" (ASCII). Das Zeichen "o" (dt.) Hat eine Dezimalposition von 111 und eine Hexadezimalposition von
6F .
01101111
dies in ein binäres System übersetzen -
01101111
. Das Symbol "k" (dt.) - Position 107 in dezimaler und
6B in hexadezimaler
01101011
in binäre
01101011
-
01101011
. Die in ASCII codierte Gesamtzeichenfolge "ok"
01101111 01101011
:
01101111 01101011
. Der Dekodierungsvorgang wird umgekehrt. Wir nehmen 8 Bits, übersetzen sie in eine 10-Dezimal-Codierung, ermitteln die Zeichennummer und sehen uns die Tabelle an, um welche Art von Zeichen es sich handelt.
Unicode
Mit den Voraussetzungen für die Erstellung einer gemeinsamen Tabelle für alle in der Welt der Charaktere, aussortiert. Nun eigentlich zum Tisch selbst. Unicode - Dies ist die Tabelle (dies ist keine Codierung, sondern eine Symboltabelle). Es besteht aus 1.114.112 Positionen. Die meisten dieser Positionen wurden noch nicht mit Symbolen besetzt, daher ist es unwahrscheinlich, dass dieser Bereich erweitert werden muss.
Dieser gesamte Speicherplatz ist in 17 Blöcke mit jeweils 65.536 Zeichen unterteilt. Jeder Block enthält eine eigene Zeichengruppe. Der Nullblock ist der grundlegende, er enthält die am häufigsten verwendeten Zeichen aller modernen Alphabete. Im zweiten Block stehen die Zeichen ausgestorbener Sprachen. Es gibt zwei Blöcke, die für den privaten Gebrauch reserviert sind. Die meisten Blöcke sind noch nicht gefüllt.
Die Gesamtkapazität von Unicode-Zeichen liegt zwischen
0 und
10FFFF (hexadezimal).
Hexadezimalzeichen werden mit dem Präfix "U +" geschrieben. Beispielsweise enthält der erste Basisblock Zeichen von U + 0000 bis U + FFFF (von 0 bis 65.535) und der letzte siebzehnte Block von U + 100.000 bis U + 10FFFF (von 1.048.576 bis 1.114.111).
Anstelle des Zoos der nationalen Kodierungen haben wir jetzt eine umfassende Tabelle, in der alle Zeichen verschlüsselt sind, die für uns nützlich sein können. Es gibt aber auch Nachteile. Wenn zuvor jedes Zeichen mit einem Byte codiert wurde, kann es jetzt mit einer anderen Anzahl von Bytes codiert werden. Um beispielsweise alle Zeichen des englischen Alphabets zu codieren, reicht ein Byte aus. Beispielsweise ist das gleiche "o" -Zeichen in Englisch Unicode U + 006F, dh die gleiche Zahl wie in ASCII ist
6F in Hexadezimal und 111 in Dezimal. Aber um das Zeichen "
U + 103D5 " (das ist die alte persische Zahl einhundert) zu codieren - 103D5 in hexadezimal und 66.517 in dezimal, brauchen wir hier drei Bytes.
Unicode-Codierungen wie UTF-8 und UTF-16 sollten dieses Problem bereits lösen. Weiter werden wir darüber sprechen.
Utf-8
UTF-8 ist eine Unicode-Codierung mit variabler Länge, mit der jedes Unicode-Zeichen dargestellt werden kann.
Sprechen wir über variable Länge. Was bedeutet das? Das erste, was zu sagen ist, ist, dass die strukturelle (atomare) Einheit dieser Codierung ein Byte ist. Die Tatsache, dass die Codierung einer Variablen lang ist, bedeutet, dass ein Zeichen mit einer anderen Anzahl von Struktureinheiten der Codierung codiert werden kann, dh mit einer anderen Anzahl von Bytes. Beispielsweise wird Latein in einem Byte und Kyrillisch in zwei Bytes codiert.
Ein bisschen abschweifen vom Thema, es ist notwendig, über die Kompatibilität von ASCII und UTF zu schreiben
Die Tatsache, dass lateinische Zeichen und grundlegende Kontrollstrukturen wie Zeilenumbrüche, Tabulatoren usw. Mit einem Byte codiert macht UTF-Codierungen kompatibel mit ASCII-Codierungen. Das heißt, die lateinische Struktur und die Kontrollstruktur befinden sich in ASCII und UTF an denselben Stellen, und die Tatsache, dass sie dort und dort von einem Byte codiert sind, stellt diese Kompatibilität sicher.
Nehmen wir das Zeichen "o" aus dem obigen ASCII-Beispiel. Denken Sie daran, dass es sich in der Tabelle der ASCII-Zeichen um 111 Stellen handelt, in
01101111
um
01101111
. In der Unicode-Tabelle lautet dieses Zeichen U + 006F, das auch in
01101111
. Und jetzt, da UTF eine Codierung mit variabler Länge ist, wird dieses Zeichen in einem Byte darin codiert. Das heißt, die Darstellung dieses Symbols in beiden Codierungen ist gleich. Dies gilt für den gesamten Zeichenbereich von 0 bis 128. Wenn Ihr Dokument also aus englischem Text besteht, werden Sie den Unterschied nicht bemerken, wenn Sie es in UTF-8 und UTF-16 und ASCII-Codierung öffnen (in UTF-16 sind dies beispielsweise alle Zeichen) wird ebenfalls in zwei Bytes codiert, sodass Sie den Unterschied nicht bemerken, wenn Ihr Editor null Bytes ignoriert usw., bis Sie anfangen, mit dem nationalen Alphabet zu arbeiten.
Vergleichen wir in der Praxis, wie der Ausdruck "Hello World" in drei verschiedenen Codierungen aussehen wird: Windows-1251 (russische Codierung), ISO-8859-1 (Codierung westeuropäischer Sprachen), UTF-8 (Unicode-Codierung). Das Wesentliche dieses Beispiels ist, dass die Phrase in zwei Sprachen geschrieben ist. Mal sehen, wie es in verschiedenen Codierungen aussehen wird.
In der Kodierung ISO-8859-1 gibt es keine solchen Zeichen "m", "und" und "p".Nun wollen wir mit den Codierungen arbeiten und sehen, wie ein String von einer Codierung in eine andere konvertiert wird und was passiert, wenn die Konvertierung falsch ist oder aufgrund der unterschiedlichen Codierungen nicht möglich ist.
Wir gehen davon aus, dass die Phrase ursprünglich in Windows-1251 kodiert wurde. Basierend auf der obigen Tabelle schreiben wir diesen Ausdruck in binärer Form, codiert in Windows-1251. Dazu müssen wir nur die Symbole von binär nach dezimal oder hexadezimal (aus der obigen Tabelle) übersetzen.
01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
Nun, dies ist die Phrase "Hello World", die in Windows-1251 codiert ist.Stellen Sie sich nun vor, Sie haben eine Datei mit Text, wissen aber nicht, in welcher Codierung sich dieser Text befindet. Sie nehmen an, dass es in ISO-8859-1 codiert ist, und öffnen es in Ihrem Editor in dieser Codierung. Wie oben gesagt, mit einem Teil der Symbole ist alles in Ordnung, sie befinden sich in dieser Kodierung und sogar an den gleichen Stellen, aber mit den Symbolen aus dem Wort "Welt" ist alles komplizierter. Diese Zeichen sind in dieser Kodierung nicht enthalten, und an ihrer Stelle in der Kodierung ISO-8859-1 handelt es sich um völlig andere Zeichen. Insbesondere ist "m" Position 236, "und" ist 232. "p" ist 240. Und an diesen Positionen in der ISO-8859-1-Codierung befinden sich die folgenden Zeichen, Position 236 - Zeichen "ì", 232 - "è", 240 - "ð"
Die in Windows-1251 codierte und in ISO-8859-1-Codierung geöffnete Phrase „Hello World“ sieht also folgendermaßen aus: „Hello ìèð“. Es stellt sich also heraus, dass diese beiden Codierungen nur teilweise kompatibel sind und es nicht richtig funktioniert, eine Zeichenfolge von einer Codierung zur anderen zu codieren, da es einfach keine solchen Zeichen gibt.
Hier werden Unicode-Codierungen benötigt, und speziell in diesem Fall ist UTF-8 zu berücksichtigen. Dass die Zeichen darin mit einer anderen Anzahl von Bytes von 1 bis 4 codiert werden können, haben wir bereits herausgefunden. Es ist jetzt erwähnenswert, dass mit UTF nicht nur 256 Zeichen, wie in den beiden vorherigen, codiert werden können, sondern nur alle Unicode-Zeichen
Es funktioniert wie folgt. Das erste Bit jedes Bytes des Kodierungszeichens ist nicht für das Zeichen selbst verantwortlich, sondern für die Bestimmung des Bytes. Das heißt, wenn beispielsweise das führende (erste) Bit Null ist, bedeutet dies, dass nur ein Byte zum Codieren eines Zeichens verwendet wird. Welche bietet Kompatibilität mit ASCII. Wenn Sie sich die ASCII-Zeichentabelle genau ansehen, werden Sie feststellen, dass die ersten 128 Zeichen (englisches Alphabet, Steuerzeichen und Interpunktionszeichen), wenn sie in Binärzeichen umgewandelt werden, mit einem Null-Bit beginnen (seien Sie vorsichtig, wenn Sie Zeichen in ein Binärsystem übersetzen, beispielsweise online) Konverter, dann kann das erste Null führende Bit verworfen werden, was verwirrend sein kann).
01001000
- das erste Bit ist Null, dann codiert 1 Byte 1 Zeichen -> "H"
01100101
- das erste Bit ist Null, was bedeutet, dass 1 Byte 1 Zeichen codiert -> "e"
Wenn das erste Bit nicht Null ist, wird das Zeichen in mehreren Bytes codiert.
Bei Doppelbyte-Zeichen sollten die ersten drei Bits - 110 sein
110 10000 10 111100
- Am Anfang von 110 codieren 2 Bytes 1 Zeichen. Das zweite Byte beginnt in diesem Fall immer mit 10.
10000111100
insgesamt die Kontrollbits (die anfänglichen, rot und grün hervorgehobenen) und übersetzen Sie alle verbleibenden
10000111100
(
10000111100
) in Hexadezimal (043C) -> U + 043C in Unicode, das Symbol „m ".
Für Drei-Byte-Zeichen im ersten Byte sind die führenden Bits 1110
1110 1000 10 000111 10 1010101
- wir fassen alles außer den
1110 1000 10 000111 10 1010101
und erhalten die hexadezimale Zahl 103V5, U + 103D5 ist die alte persische Ziffer einhundert (
10000001111010101
)
Bei 4-Byte-Zeichen im ersten Byte sind die führenden Bits 11110
11110 100 10 001111 10 111111 10 111111
- U + 10FFFF ist das letzte gültige Zeichen in der Unicode-Tabelle (
100001111111111111111
)
Auf Wunsch können wir unsere Phrase jetzt in UTF-8-Codierung aufzeichnen.
Utf-16
UTF-16 ist auch eine Kodierung mit variabler Länge. Der Hauptunterschied zu UTF-8 besteht darin, dass die Struktureinheit darin nicht nur ein, sondern zwei Bytes beträgt. Das heißt, bei der UTF-16-Codierung kann jedes Unicode-Zeichen mit zwei oder vier Bytes codiert werden. Lassen Sie mich der Klarheit halber ein Paar solcher Bytes ein Codepaar nennen. Auf dieser Grundlage kann jedes in UTF-16 codierte Unicode-Zeichen entweder mit einem oder zwei Codepaaren codiert werden.
Beginnen wir mit den Zeichen, die von einem Codepaar codiert werden. Es ist leicht zu berechnen, dass es 65.535 solcher Zeichen (2v16) geben kann, was vollständig mit dem Basis-Unicode-Block übereinstimmt. Alle Zeichen, die sich in diesem Unicode-Block in UTF-16-Codierung befinden, werden mit einem Codepaar (zwei Bytes) codiert, hier ist alles einfach.
das Symbol "o" (lateinisch) -
00000000 01101111
das Symbol "M" (kyrillisch) -
00000100 00011100
Betrachten Sie nun Zeichen außerhalb des Basis-Unicode-Bereichs. Für ihre Codierung werden zwei Codepaare (4 Bytes) benötigt. Und der Mechanismus für die Codierung ist etwas komplizierter. Gehen wir in der richtigen Reihenfolge vor.
Zunächst stellen wir die Konzepte eines Ersatzpaares vor. Ein Ersatzpaar besteht aus zwei Codepaaren, die zum Codieren eines Zeichens verwendet werden (insgesamt 4 Bytes). Für solche
Ersatzpaare wird in der Unicode-Tabelle ein spezieller Bereich von
D800 bis
DFFF zugewiesen. Dies bedeutet, dass Sie beim Konvertieren eines Codepaares von einer Byte-Form in eine hexadezimale Zahl aus diesem Bereich eine Zahl erhalten. Dies ist dann kein unabhängiges Zeichen, sondern ein Ersatzpaar.
Um ein Zeichen aus dem Bereich
10000 -
10FFFF zu codieren (
dh ein Zeichen, für das Sie mehr als ein
Codepaar verwenden müssen), benötigen Sie:
- Subtrahieren Sie 10000 (hexadezimal) vom Zeichencode (dies ist die kleinste Zahl aus dem Bereich 10000 - 10FFFF ).
- Als Ergebnis des ersten Punktes wird eine Zahl erhalten, die nicht größer als FFFFF ist und bis zu 20 Bits belegt
- Die führenden 10 Bits der empfangenen Nummer werden mit D800 summiert (Beginn des Bereichs der Ersatzpaare in Unicode).
- Die nächsten 10 Bits werden mit DC00 summiert (ebenfalls eine Zahl aus dem Bereich der Ersatzpaare )
- Danach erhalten wir 2 Ersatzpaare mit jeweils 16 Bits. Die ersten 6 Bits in jedem dieser Paare sind dafür verantwortlich, zu bestimmen, dass es sich um einen Ersatz handelt.
- Das zehnte Bit in jedem Vertreter ist für seine Reihenfolge verantwortlich, wenn es 1 ist, dann ist dies der erste Vertreter, wenn 0, dann der zweite
Wir werden dies in der Praxis analysieren, ich denke, es wird klarer.
Zum Beispiel verschlüsseln wir das Symbol und entschlüsseln es dann. Nehmen Sie die alte persische Nummer einhundert (U + 103D5):
- 103D5 - 10000 = 3D5
- 3D5 =
0000000000 1111010101
(die führenden 10 Bits haben sich als Null herausgestellt, wir bringen dies auf eine hexadezimale Zahl, wir erhalten 0 (erste zehn), 3D5 (zweite zehn)) - 0 + D800 = D800 (
110110 0 000000000
) Die ersten 6 Bits bestimmen, dass die Zahl aus dem Bereich der Ersatzpaare das zehnte Bit (rechts) Null ist. Dann ist dies der erste Ersatz - 3D5 + DC00 = DFD5 (
110111 1 111010101
) Die ersten 6 Bits bestimmen, dass die Zahl im Bereich der 110111 1 111010101
das zehnte Bit (rechts) ist, dann ist dies das zweite Ersatzbit - Insgesamt ist dieses Zeichen in UTF-16
1101100000000000 1101111111010101
Nun dekodiere das Gegenteil. Angenommen, wir haben einen solchen Code - 1101100000100010 1101111010001000:
- in hexadezimale Form übersetzen = D822 DE88 (beide Werte stammen aus dem Bereich der Ersatzpaare , daher haben wir ein Ersatzpaar vor uns)
110110 0 000100010
- Das zehnte Bit (rechts) ist Null, dann der erste Ersatz110111 1 010001000
- Das zehnte Bit (rechts) ist eins, dann der zweite Ersatz- wir verwerfen 6 Bits der für die Bestimmung des
0000100010 1010001000
, wir erhalten 0000100010 1010001000
( 8A88 ) - Addiere 10.000 (weniger Ersatzbereiche ) 8A88 + 10000 = 18A88
- Schauen Sie in der Unicode-Tabelle nach dem Zeichen U + 18A88 = Tangut Component-649. Komponenten des Tangut-Skripts.
Dank derer, die bis zum Ende lesen konnten, hoffe ich, dass es nützlich und nicht sehr langweilig war.
Hier einige interessante Links zu diesem Thema:
habr.com/de/post/158895 - nützliche allgemeine Informationen zu Kodierungen
habr.com/de/post/312642 - über Unicode
unicode-table.com/ru - die Unicode-Zeichentabelle selbst
Nun, wo wärst du eigentlich ohne sie?
de.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4 - Unicode
en.wikipedia.org/wiki/ASCII - ASCII
en.wikipedia.org/wiki/UTF-8 - UTF-8
en.wikipedia.org/wiki/UTF-16 - UTF-16