string
.
h
Etant donné le programme dont l’exécution est tracée ci dessous :
Saisissez une phrase : Les framboises sont perchees sur le tabouret de mon grand-pere. Vous avez saisi : Les framboises sont perchees sur le tabouret de mon grand-pere. Cette phrase commence par une majuscule. Cette phrase se termine par un point.
Comment faire pour saisir et manier des séquences de caractères de la sorte ?
Une chaîne de caractères est un tableau de
char
contenant un caractère nul. Le caractère nul a
0 pour code ASCII et s’écrit
’∖0’. Les valeurs significatives de la chaîne de
caractères sont toutes celles placées avant le caractère nul. On
remarque donc que si le caractère nul est en première position, on a
une chaine de caractères vide.
Par exemple, la phrase "Toto" sera codée de la sorte :
'T' |
'o' |
't' |
'o' |
0 | … |
Prenez bien note du fait que le dernier caractère de la chaine est suivi d’un caractère nul.
Comme une chaîne de caractères est un tableau de
char
, on le
déclare :
char <nom_chaine>[<taille_chaine>];
Par exemple, on déclare une chaine
c
de 200 caractères de
la sorte :
char c[200];
Attention ! Le nombre maximal de lettres qu’il sera possible
de placer dans
c
ne sera certainement pas 200 mais 199, car
il faut placer après le dernier caractère de la chaîne un caractère
nul !
On initialise une chaîne à la déclaration, et seulement à la déclaration de la sorte :
char <nom_chaine>[<taille_chaine>] = <valeur_initialisation>;
Où la valeur d’initiallisation contient la juxtaposition de caractères formant la chaîne entourée de guillemets (double quotes). Par exemple,
char c[50] = "Toto";
Cette instruction déclare une chaîne de caratères
c
initialisée à
"Toto"
. Les 5 premiers éléments du tableau
seront occupés par les 4 caractères de la chaîne ainsi que par le
caratère nul, les autres contiendront des valeurs non
significatives. Observez bien l’exemple suivant :
char c[4] = "Toto";
Cette déclaration engendrera un
warning
à la compilation et
probablement une erreur à l’exécution car l’affectation du caractère
nul à la 5-ème position du tableau donnera lieu à un débordement
d’indice.
Du fait qu’une chaine de caractère est un tableau, il est aisé d’en
isoler un élément. Ainsi
c[i]
est le
i+1
-ème élément
de la chaîne
c
. On teste donc si le premier caratère de
c
est une majuscule de la sorte :
if (c[0] >= 'A' && c[0] <= 'Z') printf("Cette phrase commence par une majuscule.\n"); else printf("Cette phrase ne commence pas par une majuscule.\n");
Cette propriété permet aussi d’afficher une chaîne caractère par caractère :
int i = 0; while(c[i] != 0) printf("%c", c[i++]);
Notez que le corps de la boucle
while
est itéré jusqu’à ce
que le caractère nul soit rencontré. Il est donc impératif que
votre chaîne se termine par le caractère nul et que le
caractère nul se trouve dans la plage d’indices du tableau. Est-ce
que le code suivant est correct ?
char c[26]; int i; for(i = 0 ; i < 26, i++) c[i] = 'a' + i;
Si la question vous est posée, vous pouvez présumer que ce code n’est pas correct. Le fait que chaque élément du tableau contienne un caractère non nul ne peut que corroborer cette présomption... Si l’on souhaite placer l’alphabet dans une chaîne, on procède de la sorte :
char c[27]; int i; for(i = 0 ; i < 26, i++) c[i] = 'a' + i; c[26] = 0;
Notez bien que le tableau contient 27 éléments si l’on compte le caractère nul, et que celui-ci est placé à la fin du tableau juste après la boucle.
Nous avons vu qu’il était possible d’utiliser le fait qu’une chaîne
est un tableau pour l’afficher. Il existe une méthode plus simple, en
utilisant
printf
avec la chaîne de format
"%s"
. Par
contre soyez attentifs au fait que si votre chaîne ne contient pas de
caractère nul ou que le caractère nul se trouve en dehors de la plage
d’indice de votre chaîne, il faudra vous attendre aux pires horreurs à
l’exécution ! Dans le code donné en exemple nous pouvons donc
écrire l’instruction d’affichage de la chaîne saisie par l’utilisateur
:
printf("Vous avez saisi :\n%s", c);
C’est maintenant que les choses se corsent, il faut être très attentif
lors des saisies : tout débordement d’indice et/ou
absence de caratère nul peut donner lieu à des bugs très
difficiles à trouver ! La plus grande vigilance est donc de
mise. Beaucoup d’amateurs utilisent des fonctions comme
gets
. Par exemple,
#include<stdio.h> #define N 20 int main() { char chaine[N]; int i; printf("Saisissez une phrase :\n"); // gets(chaine); for (i = 0 ; chaine[i] != 0 ; i++) printf("chaine[%d] = %c (code ASCII : %d)\n", i, chaine[i], chaine[i]); printf("chaine[%d] = %c (code ASCII : %d)\n", i, chaine[i], chaine[i]); return 0; }
Tout d’abord compilons ce programme :
[klaus@Isengard chaines]$ gcc -Wall mauvaiseSaisie.c -o mauvaiseSaisie.c /home/klaus/tmp/ccyKd0hf.o: In function `main': mauvaiseSaisie.c:(.text+0x24): warning: the `gets' function is dangerous and should not be used.
La première réaction du compilateur est une insulte. A-t-il raison ? Testons ce programme :
[klaus@Isengard chaines]$ ./mauvaiseSaisie Saisissez une phrase : Les framboises sont perchees sur le tabouret de mon grand pere. chaine[0] = L (code ASCII : 76) chaine[1] = e (code ASCII : 101) chaine[2] = s (code ASCII : 115) chaine[3] = (code ASCII : 32) chaine[4] = f (code ASCII : 102) chaine[5] = r (code ASCII : 114) chaine[6] = a (code ASCII : 97) chaine[7] = m (code ASCII : 109) chaine[8] = b (code ASCII : 98) chaine[9] = o (code ASCII : 111) chaine[10] = i (code ASCII : 105) chaine[11] = s (code ASCII : 115) chaine[12] = e (code ASCII : 101) chaine[13] = s (code ASCII : 115) chaine[14] = (code ASCII : 32) chaine[15] = s (code ASCII : 115) chaine[16] = o (code ASCII : 111) chaine[17] = n (code ASCII : 110) chaine[18] = t (code ASCII : 116) chaine[19] = (code ASCII : 32) chaine[20] = (code ASCII : 20) chaine[21] = (code ASCII : 0) Erreur de segmentation
Que se passe-t-il ? Des horreurs ! La fonction
gets
est la pire des choses qui puisse arriver à un programme C ! Ne
l’utilisez sous aucun prétexte ! Maintenant, nous allons envisager une
façon propre de saisir une chaîne de caractère :
fgets
. La
syntaxe est la suivante :
fgets(<chaine>, <taille>, stdin);
La taille de la chaîne saisie est limitée par
<taille>
,
caractère nul compris. Le résultat est placé dans
<chaine>
. Tous les caractères supplémentaires saisis par
l’utilisateur ne sont pas placés dans
<chaine>
, seuls les
taille−1 premiers caractères sont récupérés par
fgets
. Nous saisirons donc la phrase de notre programme de la
sorte :
fgets(c, 200, stdin);
Lorsque vous saisissez des caractères avec
fgets
, les
caractères ignorés ne sont pas éliminés du buffer. Cela signifie
qu’ils iront parasiter la prochaine saisie. Il convient donc tout
d’abord de vérifier s’il reste des caractères dans buffer et de les
éliminer si nécessaire. Commençons par le premier point : comment
s’assurer que tous les caractères saisis ont été lus ? La réponse est
très simple : grâce au caractère d’échappement
’∖n’. La saisie est validée par le caratère
d’échappement, il s’agit donc toujours du dernier caractère saisi. Le
buffer a donc été entirèrement lu si caractère d’échappement a été
lu. Si ce caractère a été lu, alors il figure juste avant le caractère
nul dans la chaîne. On le repère donc de la sorte :
while(c[i] != 0) i++; if (c[i-1] != '\n') printf("La saisie est incomplete"); else printf("La saisie est complete");
Si la chaîne
c
a été saisie correctement, aucun risque de
débordement d’indice ou de bouclage infini ne peut se présenter. La
boucle au début de l’extrait recherche le caractère nul dans la
chaîne. On sort de la boucle quand
c[i] == 0
, donc l’indice
du caratère nul est
i
. Si l’avant dernier caractère de la
chaîne, donc celui d’indice (i − 1) est le caractère
d’échappement, alors la saisie a été complète et le buffer est
vide. Sinon, c’est que des caractères ont été laissés dans le buffer
par
fgets
. Il convient donc, si l’on souhaite effectuer
d’autres saisies par la suite, de vider le buffer.
getchar
est une instruction retournant un caractère lu dans le buffer. Si le
buffer est vide,
getchar
attend une saisie de l’utilisateur
non validée par le caractère d’échappement ! On vide donc le
buffer de la sorte :
while(getchar() != '\n');
Le point-virgule à la fin de l’instruction indique au compilateur que la boucle n’a pas de corps. Cette boucle lit le buffer jusqu’à ce que le caractère d’échappement ait été lu. Pour davantage de lisibilité, on définit une macro-instruction vidant le buffer :
#define CLEAR_BUFFER while(getchar() != '\n')
Vérifiez bien avant de lancer cette instruction que le buffer n’est pas vide, sinon le programme bouclera jusqu’à ce que l’utilisateur ait saisi un caractère d’échappement.
string.h
Cette bibliothèque propose des fonctions de maniement de chaînes de caractères, à savoir :
strcmp
: comparer deux chaînes.
strlen
: longueur d’une chaîne de caractère
strsubs
: rechercher une sous-chaîne
strcat
: concaténer deux chaînes
strcpy
: copier une chaîne
Il vous est conseillé d’examiner de quelle façon fonctionnent ces fonctions, et comment elles gèrent le caractère nul.
#include<stdio.h> #define N 10 #define CLEAR_BUFFER while(getchar() != '\n') int main() { char chaine[N]; int i; printf("Saisissez une phrase :\n"); fgets(chaine, N, stdin); i = 0; while(chaine[i] != 0) i++; if (i > 0 && chaine[i-1] != '\n') CLEAR_BUFFER; else chaine[i-1] = 0; printf("Vous avez saisi :\n%s\n", chaine); if (chaine[0] >= 'A' && chaine[0] <= 'Z') printf("Cette phrase commence par une majuscule.\n"); else printf("Cette phrase ne commence pas par une majuscule.\n"); i = 0; while(chaine[i] != 0 && chaine[i] != '.') i++; if (chaine[i] == '.') if (chaine[i+1] == 0) printf("Cette phrase se termine par un point.\n"); else printf("Vous avez saisi plus d'une phrase.\n"); else printf("Cette phrase ne se termine pas par un point.\n"); return 0; }