Imprimer des caractères multi-octets dans un terminal en utilisant C

J’ai expérimenté un object chaîne personnalisé (struct) qui ressemble à ceci:

typedef struct { int encoding; int length; character * array; } EncodedSsortingng; 

L’idée est qu’en spécifiant l’encodage, je peux créer quelques fonctions qui utilisent cet encodage pour imprimer correctement la chaîne, par exemple ASCII ou utf-8 ou utf-16, etc.

En ce moment, j’essaye d’imprimer un caractère chinois (mandarin): 狗 (0x72d7). Je pensais que peut-être en l’imprimant caractère par caractère, cela fonctionnerait correctement, mais évidemment pas. Il a juste imprimé “r?” (0x72 et 0xd7, respectivement). Alors, comment puis-je modifier ce programme afin qu’il imprime le personnage?

 #include  typedef unsigned char character; typedef struct { int encoding; int length; character * array; } EncodedSsortingng; void printSsortingng(EncodedSsortingng str); int main(void) { character doginmandarin[] = {0x72U, 0xd7U}; EncodedSsortingng myssortingng = {0, sizeof doginmandarin, doginmandarin}; printSsortingng(myssortingng); printf("\n"); return 0; } void printSsortingng(EncodedSsortingng str) // <--- where I try to print the character { int i; for(i = 0; i < str.length; i++) { printf("%c", str.array[i]); } } 

Idéalement, je préférerais que le tableau contenant les caractères ne contienne que des caractères non signés, ce qui signifie la séparation des deux octets constituant le caractère. Bien que cela ne serve à rien, l’idée est d’utiliser le champ de encoding de la structure EncodedSsortingng pour déterminer le nombre d’octets de chaque caractère.

Comment cela peut-il être implémenté avec le moins de piratage possible?

Le nombre Ox72d7 est le sharepoint code Unicode (numéro abstrait) du caractère que vous souhaitez imprimer. Lorsqu’il est représenté en mémoire avec deux octets 0x72, 0xd7 , il devient le code UCS-2 pour ce caractère qui se trouve être également son codage UTF-16. Mais votre terminal attend probablement des caractères encodés en UTF-8. Le codage UTF-8 correct pour le sharepoint code Ox72d7 est 0xe7, 0x8b, 0x97 .

Vous pouvez résoudre votre code pour qu’il utilise des caractères codés UTF-8, mais ce codage est très peu pratique pour la représentation en mémoire car il génère différents nombres d’octets pour différents caractères. Cela rend les opérations de chaîne simples comme obtenir le nième caractère très compliqué. Au lieu de cela, les représentations de longueur fixe sont souvent utilisées. Par exemple, UCS-2 utilise toujours deux octets par caractère. La conversion au codage de la représentation externe est ensuite effectuée le plus tard possible, juste avant l’impression des chaînes.

EDIT (à partir des commentaires)

UTF-8 est un encodage difficile. La correspondance entre les points de code et les octets UTF-8 n’est pas anodine et implique un peu de bitume. C’est une sorte de code de Huffman, différents préfixes indiquent le nombre d’octets que le personnage occupera. De plus, tous les octets suivants commencent par 0b10 afin de détecter l’UTF-8 mal formé. C’est décrit ici: http://en.wikipedia.org/wiki/UTF-8#Description

Afin de trouver rapidement les trois octets de mon message, je viens de taper cela dans une console python: u"\u72d7".encode('UTF-8')

Vous devriez probablement vous pencher sur les fonctions de la bibliothèque c qui concernent les chaînes de caractères larges (wchar_t) et les chaînes multi-octets. L’implémentation de c-library sur Linux (ou Windows autant que je sache) est compatible avec Unicode. (Si vous en avez besoin sur votre carte microcontrôleur, vous risquez de ne pas avoir de chance). La plupart des choses qui traitent des codages utf-8 et unicode sont déjà présentes, vous n’avez donc pas besoin de le faire vous-même. Voici un exemple de comportement avec un personnage:

 #include  #include  #include  int main () { /* * use an utf-8 compatible locale. */ setlocale (LC_ALL, "en_US.utf8"); const wchar_t dog = 0x72d7; /* * wchar_t ssortingngs can contain any character. Create one * ssortingng containing only the dog. */ wchar_t in[2] = { dog, 0 }; char out[100]; /* * convert to a multibyte ssortingng, returns the number of chars. */ size_t len = wcstombs (out, in, sizeof out); printf ("the character '%lc' is %zd bytes (ssortingng: '%s')\n", dog, len, out); } 

Sortie:

 $ ./a.out the character '狗' is 3 bytes (ssortingng: '狗')