L'UTF-16 est-il à largeur fixe ou à largeur variable? Pourquoi UTF-8 n'a-t-il pas de problème d'ordre des octets?

16
  1. L'UTF-16 est-il à largeur fixe ou à largeur variable? J'ai obtenu des résultats différents de différentes sources:

    Sur http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF :

    UTF-16 stocke les caractères Unicode dans des blocs de seize bits.

    Sur http://en.wikipedia.org/wiki/UTF-16/UCS-2 :

    UTF-16 (Format de transformation Unicode 16 bits) est un codage de caractères pour Unicode capable de coder 1 112 064 [1] nombres (appelés points de code) dans l'espace de code Unicode de 0 à 0x10FFFF. Il produit un résultat de longueur variable d'une ou deux unités de code 16 bits par point de code.

  2. De la première source

    UTF-8 a également l'avantage que l'unité de codage est l'octet, donc il n'y a pas de problèmes de commande d'octets.

    Pourquoi UTF-8 n'a-t-il pas de problème d'ordre des octets? Il est de largeur variable, et un caractère peut contenir plus d'un octet, donc je pense que l'ordre des octets peut toujours être un problème?

Merci et salutations!

StackExchange pour tous
la source

Réponses:

13

(1) Que signifie la séquence d'octets, un arrary de char en C? UTF-16 est-il une séquence d'octets, ou qu'est-ce que c'est alors? (2) Pourquoi une séquence d'octets n'a-t-elle rien à voir avec une longueur variable?

Vous semblez mal comprendre ce que sont les problèmes endiens. Voici un bref résumé.

Un entier 32 bits occupe 4 octets. Maintenant, nous connaissons l'ordre logique de ces octets. Si vous avez un entier 32 bits, vous pouvez en obtenir l'octet élevé avec le code suivant:

uint32_t value = 0x8100FF32;
uint8_t highByte = (uint8_t)((value >> 24) & 0xFF); //Now contains 0x81

C'est bien beau. Le problème commence par la façon dont divers matériels stockent et récupèrent les entiers de la mémoire.

Dans l'ordre Big Endian, une mémoire de 4 octets que vous lisez en tant qu'entier 32 bits sera lue, le premier octet étant l'octet de poids fort:

[0][1][2][3]

Dans l'ordre Little Endian, une mémoire de 4 octets que vous lisez comme un entier 32 bits sera lue, le premier octet étant l' octet faible :

[3][2][1][0]

Si vous avez un pointeur sur un pointeur sur une valeur 32 bits, vous pouvez le faire:

uint32_t value = 0x8100FF32;
uint32_t *pValue = &value;
uint8_t *pHighByte = (uint8_t*)pValue;
uint8_t highByte = pHighByte[0]; //Now contains... ?

Selon C / C ++, le résultat n'est pas défini. Ce pourrait être 0x81. Ou cela pourrait être 0x32. Techniquement, il pourrait renvoyer n'importe quoi, mais pour les systèmes réels, il retournera l'un ou l'autre.

Si vous avez un pointeur sur une adresse mémoire, vous pouvez lire cette adresse en tant que valeur 32 bits, 16 bits ou 8 bits. Sur une grande machine endienne, le pointeur pointe vers l'octet haut; sur une petite machine endienne, le pointeur pointe vers l'octet bas.

Notez qu'il s'agit de lire et d'écrire sur / depuis la mémoire. Cela n'a rien à voir avec le code C / C ++ interne. La première version du code, celle que C / C ++ ne déclare pas non définie, fonctionnera toujours pour obtenir l'octet de poids fort.

Le problème est lorsque vous commencez à lire des flux d'octets. Comme à partir d'un fichier.

Les valeurs 16 bits ont les mêmes problèmes que celles 32 bits; ils n'ont que 2 octets au lieu de 4. Par conséquent, un fichier peut contenir des valeurs 16 bits stockées dans un ordre big endian ou little endian.

UTF-16 est défini comme une séquence de valeurs 16 bits . En fait, c'est un uint16_t[]. Chaque unité de code individuelle est une valeur de 16 bits. Par conséquent, afin de charger correctement UTF-16, vous devez connaître l'endian-ness des données.

UTF-8 est défini comme une séquence de valeurs à 8 bits . C'est un uint8_t[]. Chaque unité de code individuelle a une taille de 8 bits: un seul octet.

Maintenant, UTF-16 et UTF-8 permettent à plusieurs unités de code (valeurs 16 bits ou 8 bits) de se combiner pour former un point de code Unicode (un "caractère", mais ce n'est pas le terme correct; c'est une simplification ). L' ordre de ces unités de code qui forment un point de code est dicté par les codages UTF-16 et UTF-8.

Lors du traitement de l'UTF-16, vous lisez une valeur de 16 bits, en faisant la conversion endienne nécessaire. Ensuite, vous détectez s'il s'agit d'une paire de substitution; si c'est le cas, vous lisez une autre valeur de 16 bits, combinez les deux et à partir de cela, vous obtenez la valeur de point de code Unicode.

Lors du traitement UTF-8, vous lisez une valeur de 8 bits. Aucune conversion endienne n'est possible, car il n'y a qu'un seul octet. Si le premier octet indique une séquence multi-octets, vous lisez un certain nombre d'octets, comme dicté par la séquence multi-octets. Chaque octet individuel est un octet et n'a donc pas de conversion endienne. L' ordre de ces octets dans la séquence, tout comme l'ordre des paires de substitution dans UTF-16, est défini par UTF-8.

Il ne peut donc y avoir aucun problème endien avec UTF-8.

Nicol Bolas
la source
10

La réponse de Jeremy Banks est correcte dans la mesure où elle va, mais n'a pas abordé l'ordre des octets.

Lorsque vous utilisez UTF-16, la plupart des glyphes sont stockés à l'aide d'un mot à deux octets - mais lorsque le mot est stocké dans un fichier disque, quel ordre utilisez-vous pour stocker les octets constitutifs?

Par exemple, le glyphe CJK (chinois) pour le mot "eau" a un codage UTF-16 en hexadécimal de 6C34. Lorsque vous écrivez cela sur deux octets sur le disque, l'écrivez-vous comme "big-endian" (les deux octets sont 6C 34)? Ou l'écrivez-vous comme "petit-boutien (les deux octets sont 34 6C)?

Avec UTF-16, les deux ordonnances sont légitimes, et vous indiquez généralement lequel a le fichier en faisant du premier mot du fichier une marque d'ordre des octets (BOM), qui pour le codage big-endian est FE FF, et pour little-endian le codage est FF FE.

UTF-32 a le même problème et la même solution.

UTF-8 n'a pas ce problème, car il est de longueur variable, et vous écrivez efficacement la séquence d'octets d'un glyphe comme s'il s'agissait d'un petit-boutien. Par exemple, la lettre "P" est toujours codée en utilisant un octet - 80 - et le caractère de remplacement est toujours codé en utilisant les deux octets FF FD dans cet ordre.

Certains programmes mettent un indicateur à trois octets (EF BB BF) au début d'un fichier UTF-8, ce qui permet de distinguer UTF-8 des encodages similaires comme ASCII, mais ce n'est pas très courant, sauf sur MS Windows.

Bob Murphy
la source
Merci! (1) la lettre "P" n'est qu'un octet en UTF-8. Pourquoi le caractère de remplacement est-il ajouté à son code? (2) En UTF-8, il y a d'autres caractères qui ont plus d'un octet en UTF-8. Pourquoi l'ordre des octets entre les octets pour chacun de ces caractères n'est pas un problème?
StackExchange for All
@Tim: (1) Vous n'ajoutez pas le caractère de remplacement au code pour P. Si vous voyez 80 FF FD, c'est deux caractères - un caractère P et un caractère de remplacement.
Bob Murphy
(2) Vous écrivez et lisez toujours les deux octets du "caractère de remplacement" comme FF FD, dans cet ordre. Il n'y aurait un problème de commande d'octets que si vous pouviez également écrire le "caractère de remplacement" en FD FF - mais vous ne pouvez pas; cette séquence de deux octets serait autre chose qu'un "caractère de remplacement".
Bob Murphy
1
@Tim: Vous voudrez peut-être travailler sur en.wikipedia.org/wiki/UTF-8 . C'est vraiment très bien, et si vous pouvez comprendre tout cela et les autres pages Wikipédia liées à Unicode, je pense que vous trouverez que vous n'avez plus de questions à ce sujet.
Bob Murphy
4
La raison pour laquelle UTF-8 n'a aucun problème avec l'ordre des octets est que le codage est défini comme une séquence d'octets , et qu'il n'y a pas de variations avec une endianité différente. Cela n'a rien à voir avec la longueur variable.
starblue