mardi 21 avril 2020

Langage C et codage des caractères

La fonction strlen est censée compter le nombre de caractères d'une chaine. Mais strlen("déjà") ne donne pas 4 mais 6. C'est qu'elle compte le nombre de bytes et "déjà" s'écrit avec 6 bytes, à cause des lettres accentuées.

Exemple d'introduction

Le C s'accomode tant bien que mal de l'utf-8. On peut lire, écrire, copier, concaténer etc. Ça se gâte dès lors qu'il faut compter les caractères.

Exemple codage1.c :

 1 // codage1.c
 2 #include <stdio.h>
 3 int main()
 4 {
 5    char word[] = "déjà";
 6    printf("\%s est affiché correctement, mais,∖n", "déjà");
 7    printf("\%-8srate∖n", "déjà");
 8    printf("encodage de «déjà» en utf-8 : ");
 9    for(char *cp=word; *cp != 0; cp++)
10       printf("\%x ", (unsigned char)*cp);
11    puts("∖n");
12 }

Résultat :

déjà est affiché correctement, mais,
déjà  rate
encodage de «déjà» en utf-8 : 64 c3 a9 6a c3 a0 

Dans ce bout de code, la deuxième fonction printf (ligne 7) devrait laisser quatre espaces entre les mots «déjà» et «rate», or elle n'en laisse que deux.

C'est que la largeur du champ est calculée pour huit char et le mot «déjà» en compte six : 64 c3 a9 6a c3 a0.

Remarques

  1. Dans vim, avec le curseur sur le «é» tapez en mode normal g8, vous verrez sur la ligne du bas c3 e9.
  2. Utf-8 est un format de transformation (Universal Character Set Transformation Format) entre des caractères unicode et une suite d'octets (exemple : 00e9c3 e9). Mais ce n'est pas là le sujet de cet article.

Le fatras des codages

Oublions l'ASCII et l'ISO 8859-1 des débuts, et concentrons nous sur l'Unicode et ses avatars.

L'unicode est une gigantesque table de caractères regroupant tous les caractères de toutes les langues. Exemples : U+0041 est le A, U+00e9 est le é, U+21a6 est et U+1d1f1 est le 𝄟, etc.

L'utf-8 est un format de transformation d'une suite d'octets (1 à 4 octets) en caractère unicode. Ainsi 41 fait référence à 0x41, c3 e9 fait référence à 0xe9, e2 86 a6 fait référence à 0x21a6, etc. Cette transformation économise de la place, puisque la majorité des caractères latins (ASCII) sont codés sur un seul octet, mais implique un traitement spécifique pour les autres.

L'utf-8 est conçu de telle sorte qu'à tout endroit du texte on peut lire les caractères suivants.

Le défaut de l'utf-8 est qu'en C, les fonctions de la bibliothèque standard ou de la bibliothèque string présentent des difficultés pour compter le nombre de caractères.

Une solution

Nous allons convertir (en mémoire) la chaine de caractères en wchar_t, et au passage compter le nombre de caractères au sens usuel. La fonction de conversion est mbstowcs (multi-byte string to wide char string)

Fichier codage2.c :

 1 // codage2.c
 2 #include <wctype.h>
 3 #include <locale.h>
 4 #include <wchar.h>
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <string.h>
 8 #include <assert.h>
 9 
10 int main(int argc, char *argv[])
11 {
12    size_t len, mbslen; // number of multibyte characters in source
13    wchar_t *wcs; // pointer to converted wide character string
14    // défault word
15    char *str = argc == 2 ? argv[1] : "déjà";
16    // Apply the specified locale
17    assert(setlocale(LC_ALL, "") != NULL);
18    //length required to hold str converted to a wide character string
19    mbslen = mbstowcs(NULL, str, 0);
20    assert(mbslen != (size_t) -1);
21    len = strlen(str);
22    printf("\%-12s: \%u bytes∖n", "str lengh", len);
23    printf("\%-*s: \%u characters∖n", 12 + len - mbslen,
24       str, mbslen);
25 }

Cette fois la largeur de champ est calculée correctement.

str lengh   : 6 bytes
déjà        : 4 characters

C'est quand même un peu laborieux. Les anglophones n'ont pas ces misères. Eux au moins se contentent des vingt-six lettres de l'alphabet. Nous autres nous gaspillons une touche de clavier pour la lettre «ù» qui n'est utilisée que dans un seul mot : «où». De nombreux linguistes (1) ont suggéré de remplacer tous les accents par un macron. Ce serait bien le moment.

Remarque : J'abuse de la macro assert parce que j'ai la flemme de taper

if( ... ) {
   perror( ... );
   return EXIT_FAILURE;
}

Supplique

ā Monsieur Macron, Prēsident de la Rēpublique,

Monsieur le Prēsident,

C'est une affaire entendue, votre mandat tourne en eau de boudin. Afin de laisser une trace positive de votre passage ā l'Ēlysēe, je vous propose de prendre une mesure de simplification de l'orthographe.

Il vous suffira de faire paraītre dans le Journal Officiel une seule ligne disant que dēsormais tous les accents aigu, grave, circonflexe, et trēma pourront être remplacēs par une seule diacritique, le macron.

Et quel panache que cette réforme soit signēe par ... Emmanuel Macron !

Nous pourrions alors utiliser un clavier QWERTY avec une touche unique dēdiēe ā ces saletēs.

Bien ā Vous

P.S. Le texte de cette lettre a ētē ēcrit avec cette convention.

(1)

Nina Catch

«Aujourd’hui je pose la question : avons-nous besoin de deux accents, l’aigu et le grave ? ... un seul accent, horizontal, qu’on appelle couramment l’accent plat.»


Réalisé avec Qlam - LGPL