Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 27/07/2017, à 08:08

DonutMan75

[C] setlocale et mbstowcs()

Bonjour à tous,
je suis en train de travailler sur les caractères larges (chapitre 16 du livre  "Développement système sous Linux" de Christophe Blaess) et je bloque sur la compréhension de la fonction setlocale().
Pourriez-vous me donner votre avis ?

Objectif du programme :
- faire un fprintf de son premier argument
- faire une sortie "octet par octet" de son premier argument
- établir le nombre de caractères larges nécessaire pour stocker argv[1]


Code du programme :

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main (int argc, char *argv[])
{
wchar_t *warg;
int i, l;

setlocale(LC_ALL, "");

fprintf(stdout, "- Sortie brute \t: %s\n", argv[1]);

fprintf(stdout, "- Contenu \t: ");

for (i=0; argv[1][i]!='\0'; i++)
        fprintf(stdout, "%02x ", (unsigned char) (argv[1][i]));

fprintf(stdout, "\n");


l = mbstowcs(NULL, argv[1], 0)+1; // +1 pour le '\0' final...
fprintf(stdout, "- Longueur mo \t: %d\n", l); 

}

Exemples d’exécution :

$ ./test_wchar abc
- Sortie brute 	: abc
- Contenu 	: 61 62 63 
- Longueur mo 	: 4

$ ./test_wchar aéc
- Sortie brute 	: aéc
- Contenu 	: 61 c3 a9 63 
- Longueur mo 	: 4

On voit bien qu'ici le caractère "é" n'est pas un simple ASCII et est stocké sur les deux octets consécutifs "C3 A9"

Comme je ne comprenais pas à quoi servait la fonction setlocale() je l'ai commenté et dans ce cas j'obtiens :

$ ./test_wchar abc
- Sortie brute 	: abc
- Contenu 	: 61 62 63 
- Longueur mo 	: 4

$ ./test_wchar aéc
- Sortie brute 	: aéc
- Contenu 	: 61 c3 a9 63 
- Longueur mo 	: 0

Je suppose que la fonction mbstowcs() doit renvoyer -1 et ça donne au final 0 (avec le +1 du '\0')

Mes questions au final :

1) A quoi sert la fonction setlocale dans ce contexte ? Pourquoi la commenter fait planter la fonction mbstowcs() ?

2) Pourquoi est-ce que mon fprintf() fonctionne bien quand il rencontre 'é' (donc 'C3-A9) ? La doc indique bien que l'interprétation est faite int par int (cf. extrait ci-dessous). Faire un fprintf sans se préoccuper de ce que l'utilisateur a entré comme séquence (avec potentiellement des caractères UTF-8 très exotiques), ça vous parait propre ou pas ?

man fprintf a écrit :

c      If no l modifier is present, the int argument is converted to an unsigned char, and the resulting character is written.

Merci d'avance pour vos retours smile

Donut

Hors ligne

#2 Le 27/07/2017, à 08:59

pires57

Re : [C] setlocale et mbstowcs()

La fonction setlocale() est utilisée pour indiquer ou demander la localisation courante du programme. Si locale n'est pas NULL, la localisation courante du programme est modifiée en fonction des arguments.
Un appel réussi à setlocale() renvoie une chaîne correspondant à la localisation.  La chaîne est renvoyée afin qu'un appel ultérieur avec cette chaîne et la catégorie associée restituera cette partie de la localisation du processus. La valeur renvoyée est NULL si la demande ne peut pas être traitée.


Utilisateur d'Archlinux, Ubuntu et Kali Linux
Administrateur système et réseau spécialisé Linux.
LinkedIn

Hors ligne

#3 Le 27/07/2017, à 10:14

DonutMan75

Re : [C] setlocale et mbstowcs()

Hello pires57,
merci pour cette réponse.
Donc si je comprends bien ce que tu dis, l'appel à setlocale() sert ici à identifier explicitement l'encodage des chaînes de caractères *multi-octets* comme étant de l'UTF-8.
UTF-8 est l'implémentation la plus populaire de l'Unicode si je comprends bien.

Quoiqu'il en soit, c'est critique pour mbstowcs(), car si les caractères sont définis de façon standard par l'Unicode, leur implémentation "machine" peut différer...

Voici ce que dit le manuel de mbstowcs()

man mbstowcs a écrit :

NOTES
       The behavior of mbstowcs() depends on the LC_CTYPE category of the current locale.

La documentation de gnu.org indique par ailleurs :

gnu.org a écrit :

LC_CTYPE
This category applies to classification and conversion of characters, and to multibyte and wide characters; see Character Handling, and Character Set Handling.

Si je tente maintenant de regarder quels sont les LC_CTYPE possibles sur ma machine :

$ locale -c LC_CTYPE
LC_CTYPE
UTF-8

Je n'ai QUE le UTF-8 de défini, c'est bien correct ?

Quoiqu'il en soit, lorsqu'on traite des arguments passés par l'utilisateur, est-ce que l'utilisation directe et naive de fprintf (entre autres..) peut être dangereuse ?
Existe-t'il par exemple, une fonction de la GLibC qui dirait si une chaîne de caractère est ASCII ou pas ?
Suffirait-t'il de vérifier que pour chaque octet de l'argument, le bit de poids fort est à 0 (ASCII == 7 bits) ?
Comment gérez-vous cela de votre côté ? Genre si on veut imposer des arguments en ASCII pur...

Merci d'avance pour vos retours smile

Donut

Hors ligne