Licence CC 0

Jeux de caractères et encodages

Dernière mise à jour :

Lorsque nous vous avons présenté les chaînes de caractères, nous vous avons précisé que celle-ci étaient des tableaux de char terminé par un caractère nul, en vous laissant sous-entendre qu’à chaque « caractère » ('a', ';', '1', etc.) correspondait un char. Toutefois, ce n’est pas tout à fait exacte ou, plus précisément, ce n’est pas toujours vrai.

Dans ce chapitre, nous allons découvrir comment les chaînes de caractères sont réellement représentées et pour quelles raisons cette représentation est susceptible de varier.

Les jeux de caractères et les encodages

Introduction

Avant de poursuivre ce chapitre, nous vous invitons à lire au moins les deux premiers chapitres du cours de Maëlan sur les encodages. Ceux-ci constitueront une base solide sur laquelle nous nous appuierons dans la suite.

Ce que dit la norme

Maintenant que les notions de jeux de caractères et d’encodages vous sont connues, voyons comment celles-ci s’agencent en C. La norme définit deux jeux de caractères1 :

  1. Le jeu de caractères source qui, comme son nom ne l’indique pas, correspond au jeu de caractères vers lequel votre fichier source va être converti. Il ne s’agit donc pas de l’encodage de votre fichier source (que vous déterminez à l’aide de votre éditeur de texte), mais d’un encodage interne au compilateur.
  2. Le jeu de caractères d’exécution qui correspond à celui utilisé par votre système (il est par exemple utilisé par votre console) vers lequel votre programme sera finalement traduit. Ce dernier dépend de la locale employée (nous y reviendrons un peu plus tard dans ce chapitre).

Autrement dit, il y a possiblement deux conversions lors de la compilation : une qui a lieu du jeu de caractères de vos fichiers sources vers le jeu employé en interne par le compilateur et une depuis le jeu de caractères du compilateur vers celui du système.

Caractères garantis

La norme précise que ces deux jeux comprennent au minimum les caractères suivants.

1
2
3
4
5
6
7
A  B  C  D  E  F  G  H  I  J  K  L  M
N  O  P  Q  R  S  T  U  V  W  X  Y  Z
a  b  c  d  e  f  g  h  i  j  k  l  m
n  o  p  q  r  s  t  u  v  w  x  y  z
0  1  2  3  4  5  6  7  8  9
!  "  #  %  &  '  (  )  *  +  ,  -  .  /  :
;  <  =  >  ?  [  \  ]  ^  _  {  |  }  ~

À ceux-ci s’ajoutent l’espace, les tabulations horizontales et verticales et le saut de page.

Il est également précisé que les points de codes des dix chiffres ('0', '1', '2', '3', '4', '5', '6', '7', '8' et '9') doivent se suivre de manière croissante.

Jeu de caractère source

De plus, le jeu de caractères source doit comprendre un ou plusieurs caractères permettant d’indiquer la fin d’une ligne de texte.

Jeu de caractère d’exécution

Enfin, le jeu de caractères d’exécution doit comprendre le caractère nul, le caractère d’appel, l’espacement arrière, le retour chariot et le saut de ligne.

Certes, mais encore ?
C’est super cette description détaillée, mais ça m’apporte quoi de savoir ça ? J’en fais quoi, moi, de vos deux jeux ?

Pour résumer, la norme nous décrit ici les caractères qui peuvent être employés dans vos fichiers sources et les caractères que votre système doit supporter au minimum.

Ceci est impératif pour assurer d’une part la compilation de vos programmes et la bonne exécution de ceux-ci. En effet, imaginer par exemple que le compilateur utilise en interne un jeu de caractères ne comprenant pas le caractère p, vous serez bien ennuyer ensuite pour faire appel à printf()… De même, il serait fort gênant que votre console ne sache pas afficher les retours à la ligne.

Toutefois, cela a également une seconde conséquence : si vous utilisez un autre caractère que ceux énumérés ci-dessus (par exemple un « e » accent, un caractère cyrillique ou un idéogramme japonais), la norme ne vous garantit pas d’une part que la compilation réussisse (la conversion du jeu utilisé par vos fichiers sources vers celui du compilateur pourrait par exemple échouer) et, d’autre part, que ceux-ci seront supportés par votre système (si ce n’est pas le cas, cela se traduira le plus souvent par un affichage chaotique).

Aussi, dans un soucis de portabilité, il est nécessaire de se contenter le plus souvent de ces derniers, ce qui exclut donc l’emploi (ou à tout le moins l’emploi correct) de la plupart des langues du monde à l’exception de l’anglais et du latin. :-°

  1. Cette restriction doit toutefois être relativisée puisque la plupart des compilateurs utilisent l’UTF-8 en interne, de même que les systèmes d’exploitations modernes. De plus, cette restriction n’a pas d’objet pour les commentaires puisque ceux-ci sont ignorés lors de la compilation.
  2. Par ailleurs, sachez qu’il existe pas mal de solutions permettant de contourner cette limitation. À ce sujet, si cela vous intéresse, nous vous recommendons GNU gettext qui est une solution libre et gratuite très utilisée sous GNU/Linux et *BSD.

Les plus attentifs (ou fourbes, c’est selon :p ) d’entre-vous se rappelerons sans doute que plusieurs des codes présentés dans ce cours comportent des caractères accentués et… oui, nous n’avons pas respecté la norme sur ce point. Mais bon, un exemple n’est-il pas plus agréable avec ? :ange:


  1. Programming Language C, X3J11/88-090, § 2.2.1, Character sets 

Les caractères larges

Mise en situation

Si nous devons tâcher de respecter la norme en n’employant pas de caractères en dehors de ceux garantis, cela n’est pas vrai pour les utilisateurs de nos programmes qui, eux, ne se gêneront pas pour utiliser ceux supportés par leur système (c’est d’ailleurs bien là l’intérêt de choisir la langue de son système).

Or, jusqu’à présent, nous sommes toujours partis du principe que les caractères entrés tenaient sur un seul char, ce qui n’est pas toujours vrai, comme vous avez pu le voir au travers du cours de Maëlan.

En fait, il s’agit du comportement par défaut des fonctions de la bibliothèque standard. Chaque char est supposé représenter un caractère et une chaîne de caractères est censée n’être qu’une suite de char finie par un zéro.

Il est possible de s’en rendre compte à l’aide du code suivant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void)
{
    char chaine[255];
    char *nl;

    if (fgets(chaine, sizeof chaine, stdin) == NULL)
    {
        perror("fgets");
        return EXIT_FAILURE;
    }

    nl = strchr(chaine, '\n');

    if (nl != NULL)
        *nl = '\0';

    printf("Longueur : %u\n", (unsigned)strlen(chaine));
    return 0;
}
1
2
3
4
5
Bonjour   
Longueur : 7

Élégament trouvé
Longueur : 19

Comme vous le voyez, la taille de la chaîne « Élégament trouvé » n’est pas celle attendue dans notre cas (notre exemple emploie l’UTF-8 comme encodage) car ce sont les multiplets (les char, donc) qui ont été comptés et non les caractères. La bonne réponse aurait dû être 16.

Les caractères larges

Afin de résoudre ce problème, la norme C89 a introduit les caractères larges. Pour ce faire, un nouveau type a été introduit : le type wchar_t (pour wide character), défini dans l’en-tête <stddef.h>. Celui-ci n’est rien d’autre qu’un type entier (signé ou non signé) capable de représenter le point de code le plus élevé supporté par le système.

L’objectif recherché est de traduire une chaîne de caractères classique recourant à un encodage avec un nombre variable de multiplets (comme l’UTF-8 ou l’ISO-2022) vers une chaîne de caractères larges dont chacun représentera exactement un caractère. Dans le cas de chaînes de caractères en UTF-8 par exemple, celles-ci seront le plus souvent converties en UTF-16 ou en UTF-32.

À cet effet, plusieurs fonctions de conversions sont mises à notre disposition et sont définies dans l’en-tête <stdlib.h>. Toutefois, aucune fonction de traitement de ces chaînes n’est fournie, c’est-à-dire que celles-ci doivent être manipulées « à la main » (il n’y a donc par exemple pas de fonction du type strlen() qui manipule une chaîne de wchar_t).

En vérité, les normes suivantes du langage C (à commencer par un amendement adopté en 1994) ont ajouté des fonctions de traitements des chaînes de caractères larges ainsi que d’autres fonctions de conversions dites « réentrantes » (c’est-à-dire qui peuvent être appelées simultanément par plusieurs fils d’exécutions ou threads en anglais). Toutefois, nous ne les aborderons pas dans ce cours, d’une part parce que celui-ci se fonde sur la norme C89 et, d’autre part, parce que leur présentation mériterait plusieurs chapitres à elle seule.

Les fonctions mbtowc et wctomb

1
2
int mbtowc(wchar_t *destination, const char *chaine, size_t max);
int wctomb(char *destination, wchar_t source);

La fonction mbtowc() (pour multibyte character to wide character) convertit une suite de multiplets en un caractère large (qu’elle stocke dans l’objet référencé par destination) en lisant au plus max multiplets depuis la chaîne source. Elle retourne le nombre de multiplets utilisés pour produire le caractère large ou -1 en cas d’erreur.

La fonction wctomb() (pour wide character to multibyte character) effectue l’opération inverse : elle convertit le caractère large source en une suite de multiplets qui sera stockée dans le tableau destination. Elle retourne le nombre de multiplets produits en cas de succès et -1 en cas d’erreur.

La fonction mbtowc

L’exemple ci-dessous lit une ligne depuis l’entrée standard et convertit la première suite de multiplets représentant un caractère en un caractère large.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void)
{
    char chaine[255];
    wchar_t wc;
    char *nl;
    int n;

    if (setlocale(LC_CTYPE, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }
    if (fgets(chaine, sizeof chaine, stdin) == NULL)
    {
        perror("fgets");
        return EXIT_FAILURE;
    }

    nl = strchr(chaine, '\n');

    if (nl != NULL)
        *nl = '\0';

    n = mbtowc(&wc, chaine, MB_CUR_MAX);

    if (n <= 0)
    {
        perror("mbtowc");
        return EXIT_FAILURE;
    }


    printf("%d multiplet(s) a(ont) été lu pour produire la valeur %u.\n", \
    n, (unsigned)wc);
    return 0;
}
1
2
3
4
5
Élégamment trouvé
2 multiplet(s) a(ont) été lu pour produire la valeur 201.

ASCII
1 multiplet(s) a(ont) été lu pour produire la valeur 65.

Les résultats obtenus dépendent bien entendu du jeu de caractères utilisé par votre système. Si ce dernier n’emploie pas l’Unicode ou n’utilise pas l’UTF-8 comme encodage, la sortie du programme peut être différente.

Comme vous le voyez, étant donné que notre système emploie de l’UTF-8, deux multiplets ont été lus depuis la chaîne chaine pour construire la valeur du caractère É, soit 201 (qui correspond à son point de code dans le jeu de caractères Unicode). Le caractère A étant quant à lui représenté sur un seul multiplet en UTF-8, la lecture d’un seul suffit pour obtenir sa valeur, à savoir 65.

L’en-tête <locale.h> a été ajouté en vue d’utiliser la fonction setlocale() dont nous parlerons dans la prochaine section. Sachez pour l’instant qu’elle doit être appelée avant d’utiliser les fonctions de conversions.

MB_CUR_MAX est une macro (elle est éfinie dans l’en-tête <stdlib.h>) dont la valeur est déterminée par la locale courante (nous y reviendrons lorsque nous aborderons la fonction setlocale()) et correspond au nombre maximum de multiplets nécessaires pour construire un caractère large dans la locale actuelle.

La fonction wctomb

Comme nous vous l’avons dit, la fonction wctomb() effectue l’exact inverse de la fonction mbtowc(). L’exemple suivant tente de convertir le caractère large É en la suite de multiplets correspondante.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <limits.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    char tab[MB_LEN_MAX];
    int i;
    int n;

    if (setlocale(LC_CTYPE, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }

    n = wctomb(tab, L'É');

    if (n <= 0)
    {
        perror("wctomb");
        return EXIT_FAILURE;
    }

    for (i = 0; i < n; ++i)
        printf("%x ", (unsigned char)tab[i]);

    putchar('\n');
    return 0;
}
1
c3 89

Ici, nous essayons de convertir le caractère large É (notez qu’une constante de type caractère large peut être définie en la faisant précédé de la lettre l ou L) en une suite de multiplets qui sera stockée dans le tableau tab.

La taille du tableau tab a été fixée à MB_LEN_MAX, une macroconstante définie dans l’en-tête <limits.h> correspondant à la plus grande suite de multiplets pouvant représenter un caractère sur le système. Notez bien la différence avec la macro MB_CUR_MAX qui se limite à la locale courante. Par ailleurs, la valeur de MB_CUR_MAX dépendant des appels à la fonction setlocale(), elle ne peut être utilisée pour déterminer la taille d’un tableau lors de sa définition.

Une fois la conversion effectuée, nous affichons la valeur des différents multiplets en hexadécimal (remarquez la conversion en unsigned char afin d’éviter l’affichage de nombres négatifs).

Dans le cas où vous insérez une constante de type caractère large dans votre code source comme L'É', les mêmes restrictions s’appliquent que pour les caractères simples : si le caractère en question ne fait pas partie du jeu de caractères source ou d’exécution, le résultat n’est pas déterminé par la norme. Une telle pratique est donc à proscrire également, pour les mêmes motifs que précédemment.

Convertir une chaîne complète

Bien entendu, ces deux fonctions peuvent être utilisées en combinaison avec une boucle en vue de convertir une chaîne de caractères entière (c’est même là tout l’intérêt de la chose).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void)
{
    char mbs[255];
    wchar_t wcs[255];
    char *nl;
    char *pc;
    int n;
    int i;

    if (setlocale(LC_CTYPE, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }
    if (fgets(mbs, sizeof mbs, stdin) == NULL)
    {
        perror("fgets");
        return EXIT_FAILURE;
    }

    nl = strchr(mbs, '\n');

    if (nl != NULL)
        *nl = '\0';

    pc = mbs;

    for (i = 0; (n = mbtowc(&wcs[i], pc, MB_CUR_MAX)) > 0; ++i)
    {
        if (*pc == '\0')
            break;

        pc += n;
    }

    if (*pc != '\0')
    {
        perror("mbtowc");
        return EXIT_FAILURE;
    }

    for (i = 0; wcs[i] != L'\0'; ++i)
        printf("%u ", (unsigned)wcs[i]);

    putchar('\n');
    return 0;
}
1
2
Élégament trouvé                   
201 108 233 103 97 109 101 110 116 32 116 114 111 117 118 233 

Comme vous le voyez, nous appelons la fonction mbtowc() tant que celle-ci ne rencontre pas une erreur ou que nous ne rencontrons pas le caractère de fin de chaîne (ce dernier devant également être converti, cette seconde condition est placée au sein de la boucle). Ensuite, suivant le nombre de multiplets lus par mbtowc(), nous augmentons la valeur du pointeur pc afin de référencer les prochains caractères à lire. À la sortie de la boucle, nous vérifions que le pointeur pc pointe bien vers le caractère nul sans quoi cela signifie que la fonction mbtowc() a rencontré une erreur. Enfin, nous parcourons la chaînes large wcs pour afficher les différents points de code des caractères la composant.

Notez que comme la comparaison wcs[i] != L'\0' porte sur le caractère large wcs[i], nous avons fait du second opérande un caractère large également. Le caractère nul étant garanti de faire partie du jeu de caractères d’exécution, cela ne pose pas de problèmes.

Les fonction mbstowcs et wcstombs

1
2
size_t mbstowcs(wchar_t *destination, char *source, size_t max);
size_t wcstombs(char *destination, wchar_t *source, size_t max);

C’est chouette de pouvoir employer des boucles, mais cela reste assez fastidieux… Heureusement pour nous, il existe des fonctions qui se chargent de le faire pour nous. ^^
Les fonctions mbstowcs() et wcstombs() convertissent une chaîne de caractères vers une chaîne de caractères larges et inversement. Elles stockent les max premiers caractères produits dans la chaîne destination.

Les deux fonctions retournent le nombre de caractères convertis (hors caractère nul final) ou (size_t)-1 en cas d’erreurs.

Ci-dessous, un exemple de conversion recourant à la fonction mbstowcs() qui nous permet de rendre notre programme initial correct. Notez que nous avons dû réaliser notre propre version de strlen() afin de calculer la longueur de la chaîne de caractères larges obtenues.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>


static size_t
wchar_len(wchar_t *wcs)
{
    size_t i = 0;

    while (wcs[i] != L'\0')
        ++i;

    return i;
}


int main(void)
{
    wchar_t wcs[255];
    char mbs[255];
    char *nl;

    if (setlocale(LC_CTYPE, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }
    if (fgets(mbs, sizeof mbs, stdin) == NULL)
    {
        perror("fgets");
        return EXIT_FAILURE;
    }

    nl = strchr(mbs, '\n');

    if (nl != NULL)
        *nl = '\0';

    if (mbstowcs(wcs, mbs, sizeof mbs) == (size_t)-1)
    {
        perror("mbstowcs");
        return EXIT_FAILURE;
    }

    printf("Nombre de multiplets : %u\n", (unsigned)strlen(mbs));
    printf("Nombre de caractères : %u\n", (unsigned)wchar_len(wcs));
    return 0;
}
1
2
3
Élégament trouvé
Nombre de multiplets : 19
Nombre de caractères : 16

La fonction mblen

1
int mblen(char *chaine, size_t max);

La fonction mblen() lit au plus max multiplets de la chaîne chaine. Si ceux-ci forment un caractère large valide, elle retourne le nombre de multiplets qui seront utilisés pour le composer. Dans le cas contraire, elle retourne soit 0 (si le premier caractère est le caractère nul) ou -1 (la suite de multiplets ne correspond pas à un caractère large).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>


int
main(void)
{
    int n;

    if (setlocale(LC_CTYPE, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }

    n = mblen("Élégamment trouvé", MB_CUR_MAX);

    if (n > 0)
        printf("Le prochain caractère large sera composé de %d multiplet(s).\n", n);

    return 0;
}
1
Le prochain caractère large sera composé de 2 multiplet(s).

Des fonctions dans tous leurs états

Il est important de vous préciser une chose en rapport avec ces fonctions : elles disposent d’un état interne. En vérité, ceci est essentiellement important dans le cas où vous utilisez un jeu de caractères avec état comme l’ISO-2022-JP. Dans un tel cas, les fonctions de conversions doivent mémoriser la dernière séquence d’échappement afin d’effectuer correctement leur travail, ce qu’elles réalisent à l’aide de variables internes. Or, si une erreur est rencontrée ou si une autre suite de multiplets leur est donnée en cours de route, le résultat risque de s’en trouver compromis.

Dès lors, il est nécessaire de réinitialiser cet état interne après la rencontre d’une erreur ou lors d’un changement de données à traiter. Cela se réalise très simplement en fournissant comme argument une chaîne nulle à l’une des fonctions mbtowc(), wctomb() ou mblen(). Dans un tel cas, l’état interne est remis à zéro et une valeur nulle est retournée si le jeu de caractères courant n’utilise pas d’état et un nombre strictement positif sinon.

Internationalisation et localisation

Cela est resté finalement assez discret jusqu’à ce chapitre, mais en y regardant de plus près, les programmes que nous avons réalisés sont en fait destinés à un environnement anglophone. En effet, prenez par exemple les entrées : si nous souhaitons fournir un nombre flottant à notre programme, nous devons utiliser le point comme séparateur entre la partie entière et décimale. Or, dans certains pays, on pourrait vouloir utiliser la virgule à la place. Cela nous paraît moins étrange étant donné que les constantes flottantes sont écrites de cette manière en C, mais il n’en va pas de même pour nos utilisateurs.

Ce qu’il faudrait finalement, c’est que nos programmes puissent s’adapter aux usages, coutumes et langues de notre utilisateur, ce que nous avons entrevu dans la section précédente.

L’internationalisation

L’internationalisation (parfois abrégée « i18n ») est un procédé par lequel un programme est rendu capable de s’adapter aux préférences linguistiques et régionales d’un utilisateur.

La localisation

La localisation (parfois abrégée « l10n ») est une opération par laquelle un programme internationalisé se voit fournir les informations nécessaires pour s’adapter aux préférences linguistiques et régionales d’un utilisateur.

La fonction setlocale

De manière générale, les programmes que nous avons conçus jusqu’ici étaient déjà partiellement internationalisés, car la bibliothèque standard du langage C l’est dans une certaine mesure. Toutefois, nous n’avons jamais recouru à un processus de localisation pour que ceux-ci s’adaptent à nos usages. Nous vous le donnons en mille : la localisation en C s’effectue à l’aide de… la fonction setlocale().

1
char *setlocale(int categorie, char *localisation);

Cette fonction attends deux arguments : une catégorie et la localisation qui doit être employée pour cette catégorie.

Les catégories

La bibliothèque standard du C divise la localisation en plusieurs catégories, plus précisément cinq :

  1. La catégorie LC_COLLATE qui modifie le comportement des fonctions strcoll() et strxfrm() ;
  2. La catégorie LC_CTYPE qui adapte le comportement des fonctions de conversions que nous venons de voir, ainsi que les fonctions de l’en-tête <ctype.h> ;
  3. La catégorie LC_MONETARY qui influence le comportement de la fonction localeconv() ;
  4. La catégorie LC_NUMERIC qui altère le comportement des fonctions *printf() et *scanf() ainsi que des fonctions de conversions des chaînes de caractères (atof(), strtod(), etc.) en ce qui concerne les nombres flottants ;
  5. La catégorie LC_TIME qui change le comportement de la fonction strftime().

Enfin, la catégorie LC_ALL (qui n’en est pas vraiment une) représente toutes les catégories en même temps. Nous ne attarderons toutefois que sur LC_CTYPE et LC_NUMERIC dans cette section, les fonctions affectées par les autres catégories n’ayant pas été présentées à ce stade.

Les localisations

La bibliothèque standard prévoit deux localisations possibles :

  1. La localisation "C" qui correspond à celle par défaut. Celle-ci utilise les usages anglophones et part du principe que les caractères employés se limitent à ceux garantis par la norme et qu’ils sont tous représentés sur un multiplet ;
  2. La localisation "" (une chaîne vide) qui correspond à celle utilisée par votre système.

Il est également possible de fournir un pointeur nul comme localisation, auquel cas la localisation actuelle est retournée.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    char *s;

    s = setlocale(LC_ALL, NULL);
    puts(s);

    if (setlocale(LC_ALL, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }

    s = setlocale(LC_ALL, NULL);
    puts(s);
    return 0;
}
1
2
C
fr_BE.UTF-8

Comme vous le voyez, la localisation de départ est bien C.

La forme que prend la localisation dépend de votre système. Sous unixoïdes et dans notre exemple, elle prend la forme de la langue en minuscule (au format ISO 639) suivie d’un tiret bas et du pays en majuscule (au format ISO 3166-1) et, éventuellement, terminée par un point et par l’encodage utilisé.

Exemple

Nous avons déjà eu l’occasion d’expérimenter la modification de la localisation de la catégorie LC_CTYPE. Ainsi, nous avons pu préciser aux fonctions de conversion que la traduction devait s’opérer depuis et vers le jeu de caractères d’exécution complet et non uniquement le sous-ensemble défini par la norme.

L’exemple ci-dessous modifie la localisation de la catégorie LC_NUMERIC pour que les fonctions scanf() et printf() adaptent leur gestion et affichage des nombres flottants.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    double f;

    if (setlocale(LC_NUMERIC, "") == NULL)
    {
        perror("setlocale");
        return EXIT_FAILURE;
    }

    printf("Veuillez entrer un nombre flottant : ");

    if (scanf("%lf", &f) != 1)
    {
        perror("scanf");
        return EXIT_FAILURE;
    }

    printf("Vous avez entré : %f.\n", f);
    return 0;
}
1
2
3
4
5
Veuillez entrer un nombre flottant : 45,5
Vous avez entré : 45,500000.

Veuillez entrer un nombre flottant : 45.5
Vous avez entré : 45,000000.

Comme vous le voyez, après l’appel à setlocale(), seule la virgule est considérée comme séparateur de la partie entière et de la partie décimale.


En résumé
  1. La norme définit deux jeux de caractères : le jeu de caractère source, utilisé en interne par le compilateur, vers lequel vos fichiers sources sont convertis et le jeu de caractère d’exécution, utilisé par votre système, vers lequel la conversion finale aura lieu.
  2. Mise à part les caractères garantis par la norme, les caractères supportés par les jeux de caractères source et d’exécution sont indéterminés.
  3. Le type wchar_t et les fonctions de conversion associées permettent de construire des chaînes de caractères larges à partir de chaîne simple, ce qui est particulièrement utile lorsque le jeu employé par le système encode les caractères sur un nombre variable de multiplets (comme l’ISO-2022 ou l’UTF-8).
  4. Ces fonctions de conversions disposent d’un était interne qui doit être réinitialisé après une erreur ou avant chaque changement de données à traiter.
  5. La fonction setlocale() permet de modifier la localisation de certaines fonctions de la bibliothèque standard.