3 votes

Norme C99 - fprintf - conversion s avec précision

Supposons qu'il y ait seulement Norme C99 le papier et printf doit être implémentée conformément à cette norme pour fonctionner avec l'encodage UTF-16, pourriez-vous clarifier le comportement attendu de la fonction de bibliothèque s conversion avec précision spécifiée ?

la norme C99 (7.19.6.1) pour les s la conversion dit :

Si aucun modificateur de longueur l n'est présent, l'argument doit être un pointeur vers l'élément initial d'un tableau de type caractère. Les caractères du tableau sont écrits jusqu'au caractère nul final (mais sans l'inclure). Si la précision est spécifiée, le nombre d'octets écrits ne dépasse pas ce nombre. Si la précision n'est pas spécifiée ou si elle est supérieure à la taille du tableau, le tableau contient un caractère nul.

Si un modificateur de longueur l est présent, l'argument doit être un pointeur vers l'élément initial d'un tableau de type wchar_t. Les caractères larges du tableau sont convertis en caractères multi-octets (chacun comme s'il s'agissait d'un appel à la fonction wcrtomb, avec l'état de conversion décrit par un objet mbstate_t initialisé à zéro avant la conversion du premier caractère large) jusqu'à et y compris un caractère large nul final. Les caractères multi-octets résultants sont écrits jusqu'à (mais sans inclure) le caractère nul de fin (octet). Si aucune précision n'est spécifiée, le tableau doit contenir un caractère large nul. Si une précision est spécifiée, le nombre d'octets écrits ne dépasse pas ce nombre (y compris les séquences de décalage, le cas échéant), et le tableau doit contenir un caractère large nul si, pour égaler la longueur de la séquence de caractères multi-octets donnée par la précision, la fonction doit accéder à un caractère large situé une fois au-delà de la fin du tableau. En aucun cas, un caractère multi-octet partiel n'est écrit.

Je ne comprends pas bien ce paragraphe en général et l'affirmation "Si une précision est spécifiée, pas plus que ce nombre d'octets ne sont écrits" en particulier.

Par exemple, prenons la chaîne UTF-16 "TEST" (séquence d'octets : 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00).

Ce qui doit être écrit dans le tampon de sortie dans les cas suivants :

  • Si précision est de 3
  • Si précision est 9 (un octet de plus que la longueur de la chaîne)
  • Si précision est 12 (plusieurs octets de plus que la longueur de la chaîne)

Puis il y a aussi "Les caractères larges du tableau sont convertis en caractères multi-octets". Cela signifie-t-il que l'UTF-16 doit être converti en UTF-8 en premier ? C'est assez étrange dans le cas où je m'attends à travailler uniquement avec UTF-16.

1voto

Jonathan Leffler Points 299946

Convertir un commentaire en une réponse légèrement étendue.

Quelle est la valeur de CHAR_BIT dans votre mise en œuvre ?

  • Si CHAR_BIT == 8 vous ne pouvez pas gérer UTF-16 avec %s ; vous utiliseriez %ls et vous passeriez un wchar_t * comme l'argument correspondant. Il faudrait alors lire le deuxième paragraphe de la spécification.

  • Si CHAR_BIT == 16 alors vous ne pouvez pas avoir un nombre impair d'octets dans les données. Vous devez alors savoir comment wchar_t concerne char (ont-ils la même taille ? ont-ils la même signature ?) et interpréter les deux paragraphes pour aboutir à un effet uniforme - à moins que vous n'ayez décidé d'avoir wchar_t représentent UTF-32.

Le point clé est que UTF-16 ne peut pas être traité comme une chaîne de caractères C si CHAR_BIT == 8 car il y a trop de caractères utiles qui sont codés avec un octet contenant zéro, mais ces octets zéro marquent la fin d'une chaîne de caractères à terminaison nulle. Pour gérer l'UTF-16, il faut utiliser l'option char doit être un type de 16 bits (ou plus) (donc CHAR_BIT > 8 ), ou vous devez utiliser wchar_t (et sizeof(wchar_t) > sizeof(char) ).

Notez que la spécification prévoit que les caractères larges seront convertis en une représentation multi-octet appropriée.

Si vous souhaitez que les caractères larges soient affichés en mode natif, vous devez utiliser la fonction fwprintf() et la fonction connexe de <wchar.h> défini pour la première fois dans C99. La spécification de cette dernière a beaucoup en commun avec la spécification de fprintf() mais il existe (sans surprise) des différences importantes.

7.29.2.1. La fonction fwprintf

s
Si non l est présent, l'argument est un pointeur sur l'élément initial d'un tableau de élément initial d'un tableau de caractères contenant une séquence de caractères multi-octets commençant dans l'état de décalage initial. Les caractères du tableau sont convertis comme si par des appels répétés à la fonction mbrtowc avec l'état de conversion décrit par un mbstate_t initialisé à zéro avant que le premier caractère multi-octet est converti, et écrit jusqu'à (mais sans inclure) la chaîne large nulle terminale. caractère large nul de fin. Si la précision est spécifiée, pas plus de ce nombre de caractères larges sont écrits. Si la précision n'est pas spécifiée ou si elle est supérieure à la taille du tableau converti, le tableau converti doit contenir un caractère large nul. caractère large nul.

Si un l est présent, l'argument doit être un pointeur vers la valeur initiale de l'élément d'un tableau de wchar_t type. Les caractères larges du tableau sont écrits jusqu'à (mais sans inclure) un caractère large nul de fin de tableau. Si la précision est spécifiée, le nombre de caractères larges écrits est limité à ce nombre. Si la précision n'est pas spécifiée ou est supérieure à la taille du tableau, le tableau doit contenir un caractère large nul. doit contenir un caractère large nul.

1voto

nwellnhof Points 7740

wchar_t n'est pas destiné à être utilisé pour UTF-16, mais uniquement pour les codages à largeur fixe définis par l'implémentation en fonction de la locale actuelle. Il n'y a tout simplement aucun moyen raisonnable de prendre en charge un codage de longueur variable avec l'API de caractères larges. De même, la représentation multi-octet utilisée par des fonctions telles que printf o wcrtomb est définie par l'implémentation. Si vous voulez écrire du code portable en utilisant Unicode, vous ne pouvez pas compter sur l'API de caractères larges. Utilisez une bibliothèque ou créez votre propre code.

Pour répondre à votre question : fprintf avec le l accepte une chaîne de caractères large dans l'encodage défini par l'implémentation et spécifié par la locale actuelle. Si wchar_t est de 16 bits, cet encodage pourrait être une abâtardissement de l'UTF-16, mais comme je l'ai mentionné ci-dessus, il n'y a aucun moyen de supporter correctement les substituts de l'UTF-16. Ce wchar_t est ensuite convertie en une chaîne de caractères à plusieurs octets char dans un encodage défini par l'implémentation. Il peut s'agir ou non d'UTF-8. La précision spécifiée limite le nombre de char dans la chaîne de sortie avec la restriction supplémentaire qu'aucun caractère multi-octet partiel n'est écrit.

Voici un exemple. Supposons que le codage large des caractères est UTF-32 avec des caractères 32 bits. wchar_t et que l'encodage multi-octets est UTF-8 (comme sous Linux avec un fichier locale appropriée ). Le code suivant

wchar_t w[] = { 0x1F600, 0 }; // U+1F600 GRINNING FACE
printf("%.3ls", w);

n'imprimera rien du tout puisque la séquence UTF-8 résultante comporte quatre octets. Seulement si vous spécifiez une précision d'au moins quatre

printf("%.4ls", w);

le caractère sera imprimé.

EDIT : Pour répondre à votre deuxième question, non, printf ne doit jamais écrire un caractère nul. Cette phrase signifie seulement que, dans certains cas, un caractère nul est nécessaire pour spécifier la fin de la chaîne et éviter les surlectures de la mémoire tampon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X