web-dev-qa-db-fra.com

Conversion manuelle des points de code Unicode en UTF-8 et UTF-16

J'ai un examen de programmation universitaire à venir, et une section est sur l'unicode.

J'ai vérifié partout pour trouver des réponses à cette question, et mon conférencier est inutile, donc ce n'est pas utile, c'est donc un dernier recours pour vous les gars.

La question sera quelque chose comme:

La chaîne 'mЖ 丽' a ces points de code unicode U+006D, U+0416 et U+4E3D, avec des réponses écrites en hexadécimal, encodez manuellement la chaîne en UTF-8 et UTF-16.

Toute aide sera grandement appréciée alors que j'essaie de comprendre ce problème.

36
RSM

Sensationnel. D'une part, je suis ravi de savoir que les cours universitaires enseignent à la réalité que les encodages de caractères sont un travail difficile, mais en fait, connaître les règles de codage UTF-8 semble attendre beaucoup. (Cela aidera-t-il les étudiants réussir le test de la Turquie ?)

La description la plus claire que j'ai vue jusqu'à présent pour les règles de codage des points de code UCS en UTF-8 provient de la page de manuel utf-8(7) sur de nombreux systèmes Linux:

Encoding
   The following byte sequences are used to represent a
   character.  The sequence to be used depends on the UCS code
   number of the character:

   0x00000000 - 0x0000007F:
       0xxxxxxx

   0x00000080 - 0x000007FF:
       110xxxxx 10xxxxxx

   0x00000800 - 0x0000FFFF:
       1110xxxx 10xxxxxx 10xxxxxx

   0x00010000 - 0x001FFFFF:
       11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

   [... removed obsolete five and six byte forms ...]

   The xxx bit positions are filled with the bits of the
   character code number in binary representation.  Only the
   shortest possible multibyte sequence which can represent the
   code number of the character can be used.

   The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well
   as 0xfffe and 0xffff (UCS noncharacters) should not appear in
   conforming UTF-8 streams.

Il pourrait être plus facile de se souvenir d'une version "compressée" du graphique:

Les octets initiaux démarrent des points de code déformés commencent par un 1, et ajoutez un rembourrage 1+0. Les octets suivants commencent 10.

0x80      5 bits, one byte
0x800     4 bits, two bytes
0x10000   3 bits, three bytes

Vous pouvez dériver les plages en prenant note de combien espace vous pouvez remplir avec les bits autorisés dans la nouvelle représentation:

2**(5+1*6) == 2048       == 0x800
2**(4+2*6) == 65536      == 0x10000
2**(3+3*6) == 2097152    == 0x200000

Je sais [~ # ~] i [~ # ~] je me souviens des règles pour dériver le graphique plus facilement que le graphique lui-même. Nous espérons également que vous vous souvenez bien des règles. :)

Mise à jour

Une fois que vous avez construit le graphique ci-dessus, vous pouvez convertir les points de code Unicode d'entrée en UTF-8 en trouvant leur plage, en convertissant de l'hexadécimal en binaire, en insérant les bits selon les règles ci-dessus, puis en les reconvertissant en hex:

U+4E3E

Cela s'inscrit dans le 0x00000800 - 0x0000FFFF intervalle (0x4E3E < 0xFFFF), donc la représentation sera de la forme:

   1110xxxx 10xxxxxx 10xxxxxx

0x4E3E est 100111000111110b. Déposez les bits dans le x ci-dessus (commencez par la droite, nous remplirons les bits manquants au début avec 0):

   1110x100 10111000 10111110

Il reste un x au début, remplissez-le avec 0:

   11100100 10111000 10111110

Convertir de bits en hex :

   0xE4 0xB8 0xBE
49
sarnold

Les descriptions sur Wikipedia pour TF-8 et TF-16 sont bonnes:

Procédures pour votre exemple de chaîne:

UTF-8

UTF-8 utilise jusqu'à 4 octets pour représenter les points de code Unicode. Pour le cas à 1 octet, utilisez le modèle suivant:

UTF-8 1 octet = 0xxxxxxxpoubelle = 7 bits = 0-7Fhex

L'octet initial de 2, 3 et 4 octets UTF-8 commence par 2, 3 ou 4 bits un, suivi par un bit zéro. Les octets de suivi commencent toujours par le modèle à deux bits 10, laissant 6 bits pour les données:

UTF-8 à 2 octets = 110xxxxx 10xxxxxxpoubelle = 5 + 6 (11) bits = 80-7FFhex
UTF-8 à 3 octets = 1110xxxx 10xxxxxx 10xxxxxxpoubelle = 4 + 6 + 6 (16) bits = 800-FFFFhex
UTF-8 à 4 octets = 11110xxx 10xxxxxx 10xxxxxx 10xxxxxxpoubelle = 3 + 6 + 6 + 6 (21) bits = 10000-10FFFFhex

Les points de code Unicode ne sont pas définis au-delà de 10FFFFhex.

Vos points de code sont U + 006D, U + 0416 et U + 4E3D nécessitant des séquences UTF-8 à 1, 2 et 3 octets, respectivement. Convertissez en binaire et assignez les bits:

U + 006D = 1101101poubelle = 1101101poubelle = 6Dhex
U + 0416 = 10000 010110poubelle = 11010000 dix010110poubelle = D0 96hex
U + 4E3D = 0100 111000 111101poubelle = 11100100 dix111000 dix111101poubelle = E4 B8 BDhex

Séquence d'octets finale:

6D D0 96 E4 B8 BD

ou si des chaînes terminées par nul sont souhaitées:

6D D0 96 E4 B8 BD 00

UTF-16

UTF-16 utilise 2 ou 4 octets pour représenter les points de code Unicode. Algorithme:

U + 0000 à U + D7FF utilise 0000 sur 2 octetshex à D7FFhex
U + D800 à U + DFFF sont des points de code non valides réservés pour UTF-16 à 4 octets
U + E000 à U + FFFF utilise E000 sur 2 octetshex à FFFFhex

U + 10000 à U + 10FFFF utilise un UTF-16 à 4 octets codé comme suit:

  1. Soustraire 10000hex du point de code.
  2. Résultat express sous forme de binaire 20 bits.
  3. Utilisez le modèle 110110xxxxxxxxxx 110111xxxxxxxxxxpoubelle pour coder les 10 bits supérieur et inférieur en deux mots de 16 bits.

Utilisation de vos points de code:

U + 006D = 006Dhex
U + 0416 = 0416hex
U + 4E3D = 4E3Dhex

Maintenant, nous avons un autre problème. Certaines machines stockent d'abord les deux octets d'un octet de poids faible de Word 16 bits (machines dites little-endian) et certaines stockent l'octet de poids fort en premier (machines big-endian). UTF-16 utilise le point de code U + FEFF (appelé la marque d'ordre des octets ou BOM) pour aider une machine à déterminer si un flux d'octets contient du UTF-16 à gros ou petit bout:

big-endian = FE FF 00 6D 04 16 4E 3D
petit-boutien = FF FE 6D 00 16 04 3D 4E

Avec nul-terminaison, U + 0000 = 0000hex:

big-endian = FE FF 00 6D 04 16 4E 3D 00 00
petit-boutien = FF FE 6D 00 16 04 3D 4E 00 00

Étant donné que votre instructeur n'a pas donné de point de code nécessitant un UTF-16 à 4 octets, voici un exemple:

U + 1F031 = 1F031hex - 10000hex = F031hex = 0000111100 0000110001poubelle =
1101100000111100 1101110000110001poubelle = D83C DC31hex

39
Mark Tolonen

Le programme suivant fera le travail nécessaire. Il n'est peut-être pas suffisamment "manuel" pour vos besoins, mais vous pouvez au moins vérifier votre travail.

#!/usr/bin/Perl

use 5.012;
use strict;
use utf8;
use autodie;
use warnings;
use warnings    qw< FATAL utf8 >;
no warnings     qw< uninitialized >;
use open        qw< :std :utf8 >;
use charnames   qw< :full >;
use feature     qw< unicode_strings >;

use Encode              qw< encode decode >;
use Unicode::Normalize  qw< NFD NFC >;

my ($x) = "mЖ丽";

open(U8,">:encoding(utf8)","/tmp/utf8-out");
print U8 $x;
close(U8);
open(U16,">:encoding(utf16)","/tmp/utf16-out");
print U16 $x;
close(U16);
system("od -t x1 /tmp/utf8-out");
my $u8 = encode("utf-8",$x);
print "utf-8: 0x".unpack("H*",$u8)."\n";

system("od -t x1 /tmp/utf16-out");
my $u16 = encode("utf-16",$x);
print "utf-16: 0x".unpack("H*",$u16)."\n";
4
Seth Robertson