• 2 La fonction scanf et le buffer de caractères

      Comment fonctionne la fonction scanf ?

      Avant d’aborder la gestion des caractères en C, il est important de revenir sur le fonctionnement de la lecture clavier en général sur un OS (Operating System ou Système d’exploitation en français). Pour Windows (mais c’est identique sur un MAC ou Linux), chaque appui sur une touche du clavier est capté par le driver de clavier et placé dans un buffer de caractères (mémoire RAM du PC). Par exemple, si l’utilisateur tape sur son clavier 12.3 on trouvera dans le buffer de caractères les codes ASCII des 4 caractères ('1', '2', '.', '3') suivis du code ASCII de la touche Entrée (qui a le code '\n'). Ce fonctionnement est indépendant de l’appel de la fonction scanf puisqu’il est géré par l’OS.

      Lors de l’exécution de notre programme, la fonction scanf va aller lire le buffer de caractères et attendre de trouver le code ASCII de la touche Entrée ('\n') ou la touche espace ' '. La fonction scanf est une fonction bloquante (l’exécution du code est bloquée tant que l’utilisateur n’a pas tapé sur la touche Entrée ).

      Après appui sur Entrée la fonction scanf va lire le buffer de caractères et encoder la valeur réelle (ou entière) dans la case mémoire de la valeur à lire puis va vider le buffer de caractères (en réalité c’est un pointeur qui gère le buffer).

      • 1* : chaque appui sur les touches du clavier vient remplir le buffer de caractères
      • 2* : la fonction scanf étant bloquante, l'appui sur retour charriot ('\n') vient lancer la routine de lecture du buffer de caractères et encode la variable dans le type spécifié (ici %f donc codage de type float).          

      En résumé, c’est l’OS qui s’occupe de remplir le buffer de caractères et c’est la fonction scanf (fonction bloquante) qui vient lire le buffer, encoder la valeur à lire et vider le buffer. Il faut appuyer sur la touche Entrée pour débloquer la fonction scanf.

      Chaque touche du clavier possède un code sur 8 bits basé sur le code ASCII pour les 128 premiers caractères (lettres minuscules et majuscules sans accent, chiffres et symboles usuels).

      Peut-on lire directement les caractères entrés par l’utilisateur ?

      La réponse est bien évidemment OUI. Pour lire un caractère au clavier, on utilise la fonction getchar() ou scanf("%c", &c).Ces 2 fonctions ont le même fonctionnement. Elles sont bloquantes, c’est-à-dire qu’elles attendent que l’utilisateur ait tapé sur « Entrée » pour exécuter la suite du programme. Pour afficher sur l’écran un caractère, on utilisera les fonctions putchar(c) ou printf("%c", c).

      • 1* : chaque appui sur les touches du clavier vient remplir le buffer de caractères
      • 2* : la fonction scanf étant bloquante, l'appui sur retour charriot ('\n') vient lancer la routine de lecture du buffer de caractères qui est lu pour chaque scanf caractère par caractère.    

       En résumé :à la différence avec scanf %d ou %f (encodage des caractères pour générer la valeur entière ou réelle entrée) le scanf %c vient lire le caractère courant (son code ASCII) et le place dans la variable caractère (codée sur 8 bits). 

      Le scanf étant bloquant si l'on entre un caractère (suivi de retour charriot \n'), l'utilisation de scanf %c viendra lire le code ASCII du caractère suivi du code ASCII du retour charriot ('\n'), soit 2 caractères.

      Quizz
    • 3. Lecture ou affichage de caractères

      Plusieurs solutions permettant de lire ou d'écrire des caractères :

      Fonction de lecture caractère
      Fonction d'affichage caractère

      Prenons un premier exemple d'affichage de caractères de 3 façons :

      • sous forme décimale (%d)
      • sous forme hexadécimale (%x)
      • sous forme caractère (%c) c'est à dire le caractère associé à la table ASCII.

      On peut voir que le caractère 'a' a le code ASCII 0x61 ou 97 et que la touche retour charriot '\n' a le code ASCII 0xA ou 10.

      Exemple
      Résultat

          char c = 'a';

          printf("%d 0x%X %c\n", c, c, c);

          c = 0x61;

          printf("%d 0x%X %c\n", c, c, c);

          c = '\n';

          printf("%d 0x%X %c", c, c, c);

      97 0x61 a

      97 0x61 a

      10 0xA

      Quizz
    • 4. Table ASCII

      La table ASCII donnée ci-dessous donne le code de tous les caractères de 0 à 127 (0x7F). Cette table ASCII est découpée en plusieurs parties :

      • du code 0 au code 0x1F, on trouve les caractères de controle et notamment LF (Line Feed) qui en C correspond au code '\n' ou 0x0A, CR (Carriage Return) qui en C correspond au code '\r' ou 0x0D
      • du code 0x20 au code 0x2F, on trouve des caractères imprimables  SP (SPace) qui en C a le code ' ' mais aussi  '!'  ou 0x21
      • du code 0x30 au code 0x39 , on trouve le code ASCII des chiffres '0' à '9' 
      • du code 0x41 à 0x5A , on trouve les lettres majuscules de 'A' à 'Z'
      • du code 0x61 à 0x7A, on trouve les lettres minuscules de 'a' à 'z'

       

      Voici quelques caractères non imprimables de 7 à 13 qui peuvent être utiles en C, citons par exemple '\a' pour générer un bip, r\r' pour revenir à la ligne (si l'on veut afficher du texte au même endroit (et écraser le texte précédent),'\t' pour déplacer le curseur d'une tabulation,...

      Quizz
    • 5. test sur les caractères

      Nous avons vu que les chiffres, lettres majuscules et minuscules se suivent dans la table ASCII. Il sera donc possible d'effectuer des tests en utilisant les spécificités de la table ASCII.

      Test
      Ligne de code
      Est un chiffre ?
      (c >= '0' && c <= '9')
      Est une lettre minuscule ?
      (c >= 'a' && c <= 'z')
      Est une lettre majuscule ?
      (c >= 'A' && c <= 'Z')
      Est une lettre ?
      (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
      Est un caractère de controle ?
      (c <=31 || c==127)

      Modification
      Langage C

      Passer de lettre minuscule en majuscule

      c = c + 'A' - 'a';

      Passer de lettre majuscule en minuscule

      c = c + 'a' - 'A';

      2 exemples permettant de tester des voyelles (minuscules et/ou majsucules).

      Exemple de programme de test de voyelles (lettres minuscules) 

       char c;

          printf("entrer un caractere:");

          c = getchar();

          if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y')

              printf("voyelle et minuscule");

          else

              printf("autre");

      Exemple de programme de test de voyelles (tout type de lettres minuscules ou majuscules)

       char c;

          printf("entrer un caractere:");

          c = getchar();

          if (c >= 'A' && c <= 'Z')//si lettre majuscule

              c = c + 'a' - 'A';// passage en minuscule

          if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y')

              printf("voyelle");

          else

              printf("autre");

      Quizz
    • 6. Lecture de plusieurs caractères

      Dans l’exemple ci-dessous, si l’utilisateur a entré plusieurs caractères au clavier (« ABC »), le programme va lire tous les codes ASCII présents dans le buffer jusqu’au caractère '\n' qui est le caractère « Entrée » (fin de la saisie). Il va alors compter le nombre de caractères A entrés au clavier et afficher ce nombre de caractères.

      Prenons un deuxième exemple qui veut lire 2 caractères et tester si ces 2 caractères sont identiques. N'oublions pas que pour débloquer les fonctions de lecture getchar ou scanf %c il faut que l'utilisateur tape sur Entrée. Le programme ci-dessous va vider le buffer de caractère while (getchar()!='\n'); aprés avoir lu le premier caractère, puis aprés avoir lu le deuxième caractère vider une nouvelle fois le buffer (pas nécessaire si le programme s'arrête ensuite). 

      #include <stdio.h>

      int main()

      {

          char c1;

          char c2;

          printf("taper 1 caractere puis sur Entree:");

          c1 = getchar();

          while (getchar() != '\n'); //vider le buffer de caractere

          printf("taper 1 caractere puis sur Entree:");

          scanf("%c", &c2);

          while (getchar() != '\n');//vider le buffer de caractere

          if (c1 == c2)

              printf("ok");

          else

              printf("ko");

          return 0;

      }

      Terminons avec un dernier programme qui va lire tous les caractères entrés par l'utilisateur jusqu'au caractère de tabulation (qui a le code 9 et non 10 qui est le code du retour charriot). Il va alors analyser tous les caractères lu et afficher leur code en décimal, hexadécimal et le caractère associé.

      Programme de test pour plusieurs caractères
      Texte entré :  abz95 &Z        apres tab

      int main() {

          char c;

          do {

              c = getchar();

              if (c >= '0' && c <= '9')

                  printf("un nombre : ");

              else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))

                  printf("une lettre : ");

              else

                  printf("autre chose : ");

              printf("%c %d 0X%X\n", c, c, c);

          } while (c != 9); //touche tab

      }

      une lettre : a 97 0X61

      une lettre : b 98 0X62

      une lettre : z 122 0X7A

      un nombre : 9 57 0X39

      un nombre : 5 53 0X35

      autre chose :   32 0X20

      autre chose : & 38 0X26

      une lettre : Z 90 0X5A

      autre chose :    9 0X9

      Quizz
    • 7. bibliothèque de fonctions ctype.h

      La bibliothèque ctype.h propose des fonctions de test de caractères qui peuvent être utilisés à la place de tests utilisant les codes des caractères.

      Tests
      En langage C  (fonctions de ctype.h)
      Est un chiffre ?

      int isdigit(int character);

      Est une lettre minuscule ?

      int islower(int character);

      Est une lettre majuscule ?

      int isupper(int character);

      Est une lettre ?

      int isalpha(int character);

      Est un caractère de controle

      int iscntrl(int character);

      Modifications
      En langage C  (fonctions de ctype.h)

      Passer de lettre minuscule en majuscule

      int toupper(int character);

      Passer de lettre majuscule en minuscule

      int tolower(int character);

    • 8. Récapitulatif

      Il existe donc 3 solutions permettant de répondre à un problème associant des tests sur les caractères :

      • La première solution consiste à utiliser les spécificités du codage des caractères dans la fonction main
      • La deuxième solution consiste à écrire des fonctions permettant de tester les caractères
      • La troisième solution utilise les fonctions de la bibliothèque ctype.h
      Solution 1
      Solution avec fonction 
      Solution avec fonctions de ctype.h

      int main() {

        char c;

        do {

           c = getchar();

           if (c >= '0' && c <= '9')

              printf("un chiffre : ");

           printf("%c %d 0X%X\n", c, c, c);

        } while (c != '\n);

      }

      #include <stdio.h>

      int isDigit(char c) {

        return (c >= '0' && c <= '9');

      }

      int main() {

        char c;

        do {

           c = getchar();

           if (isDigit(c))

             printf("un chiffre : ");

           printf("%c %d 0X%X\n", c, c, c);

        } while (c != '\n');

      }

      #include <stdio.h>

      #include <ctype.h>

      int main() {

        char c;

        do {

           c = getchar();

           if (isdigit(c))

              printf("un chiffre : ");

           printf("%c %d 0X%X\n", c, c, c);

        } while (c != '\n');

      }

    • 9. Test de fin de chapitre

    • 8.1 Code ASCII et caractères
      8.2 Lecture de plusieurs caractères