Chapitre 4 : Chaînes de caractères

publicité
Chapitre 4 : Chaînes de caractères
En C, on peut présenter une chaîne de caractères sous forme de :
1. tableau des caractères
ou de
: char nom [20] ;
2. pointeur vers le type caractère : char * telephone ;
1) Schéma de représentation d'une chaîne de caractères :
Si la valeur d'une chaîne de caractères nommée urgent vaut "911"
par exemple, on la présente avec le schéma suivant :
urgent ----->
| '9' | '1' | '1' |'\0'|
|_____|_____|_____|____|
La chaîne se termine par le caractère invisible '\0' qui marque sa fin.
2) Déclaration, initialisation et affectation :
Écrire d'autres manières différentes pour déclarer la chaîne
urgent et donner la valeur "911" à cette chaîne.
Solution :
1. Sous forme tableau des caractères :
a. Manière 1 :
char urgent[4] ; /* pour le caractère '\0' aussi */
strcpy(urgent, "911"); /* string copy : copie une chaîne à
une autre chaîne (string.h)
*/
Il est intéressant de noter que l'affectation suivante
est invalide :
urgent = "911" ;
urgent est le nom d'un tableau, il n'est pas une "lvalue"
(left value : valeur à gauche une affectation).
Chapitre 4 : Fichiers de type texte
Page 83
b. Manière 2 : déclarer et "initialiser"
char urgent[4]
= "911" ;
c. Manière 3 : déclarer et "initialiser"
char urgent[4]
= { '9', '1', '1', '\0' } ;
d. Manière 4 : déclarer et "affecter"
char urgent[4]
urgent[0]
urgent[1]
urgent[2]
urgent[3]
=
=
=
=
;
'9' ;
'1' ;
'1' ;
'\0';
e. Manière 5 : déclarer et lire sa valeur
char urgent[4]
;
printf("Quel est le numéro pour l'urgence ? ");
scanf("%s", urgent); /* ou gets(urgent) ; avec <string.h> */
Notez que la lecture avec scanf n'est pas assez pratique :
char nomPre [20] ;
printf("Entrez le nom et prénom ");
scanf("%s", nomPre);
printf("Le nom et prénom lu :
%s\n", nomPre);
Si le nom et prénom tapé est Cloutier Gilles
la valeur de nomPre est "Cloutier" seulement! : l'espace
entre Cloutier et Gilles provoque la fin de la saisie. On
perd une partie des informations.
Chapitre 4 : Fichiers de type texte
Page 84
2. Sous forme pointeur vers le type char
char * urgent ;
On peut faire des choses semblables comme avec un tableau
des caractères :
char * urgent = "911" ;
OU
char * urgent ;
......
strcpy(urgent , "911");
OU
char * urgent ;
......
gets(urgent);
De plus, on peut affecter (qui n'est pas le cas d'un tableau) :
urgent = "911" ; /* correct ici! */
Cependant, il est préférable d'allouer la mémoire avant de faire
l'affectation et surtout de la lecture :
urgent = (char *) malloc (4) ;
/* avec <stdlib.h> */
urgent = "911" ;
3) Affichage du contenu d'une chaîne de caractères :
Soit char * urgent = "911" ;
OU
char urgent[4] = "911" ;
Longueur d'une chaîne (strlen : string length) :
strlen("911") vaut 3 (ne pas tenir compte de '\0')
strlen(urgent) vaut 3
Affichage d'une chaîne (code de format %s) :
L'instruction :
printf("Numéro pour l'urgence : %s\n", urgent);
fait afficher :
Numéro pour l'urgence : 911
Chapitre 4 : Fichiers de type texte
Page 85
Mise en garde :
L'instruction :
printf("Numéro pour l'urgence : %c\n", *urgent);
fait afficher seulement :
Numéro pour l'urgence : 9
Pourquoi ?
urgent est un pointeur vers le type char :
| '9' | '1' | '1' |'\0'|
urgent -------> |_____|_____|_____|____|
*urgent
*urgent est de type char,
*urgent vaut '9' ici.
4) La concaténation des chaînes de caractères :
char * ch1 , * ch2 ;
ch1 = (char *) malloc(80);
ch1 = "Bonjour ";
ch2 = "tout le monde!";
Après l'instruction :
strcat(ch1, ch2) ;
ch1 vaut "Bonjour tout le monde!".
Après l'instruction :
strncat(ch1, ch2, 3) ;
ch1 vaut "Bonjour tou".
5) La copie des chaînes de caractères :
a) char * strcpy(char * destination, char * source)
Exemple :
...
strcpy(nomPre,"LALIBERTE PIERRE");
printf("%s", nomPre);
Chapitre 4 : Fichiers de type texte
Page 86
Le bloc affiche :
LALIBERTE PIERRE
b) char * strncpy (char * destination,char * source, int k)
(on copie jusqu'à k caractères au maximum)
Exemple :
...
strncpy(nomPre,"LALIBERTE PIERRE",4);
printf("%s", nomPre);
Le bloc affiche :
LALI
6) La comparaison des chaînes de caractères :
a) int strcmp(char * chaine1, char * chaine2)
valeur retournée par la fonction
< 0
0
> 0
signification
chaine1 < chaine2
chaine1 = chaine2
chaine1 > chaine2
Exemple :
...
strcmp("Bon", "Bourse") est inférieur à 0 car 'n' < 'u'
(les 2ères lettres sont pareilles)
strcmp("Bon", "Bon")
vaut 0
strcmp("papa", "GRAND")
(code ASCII)
est supérieur à zéro car 'p' > 'G'
b) int strncmp(char * chaine1,char * chaine2, int k)
Comme strcmp en se limitant aux k premiers caractères.
Exemple :
...
strncmp("Bon", "Bourse", 2)
est 0
strncmp("Bon", "Bourse", 3)
est < 0
etc ...
c) int stricmp (char * chaine1, char * chaine2)
int strincmp(char * chaine1, char * chaine2, int k)
Elles fonctionnent comme les deux dernières fonctions sans
tenir compte de la différence entre les majuscules et les
minuscules.
Chapitre 4 : Fichiers de type texte
Page 87
Exemple :
...
strincmp("Bon", "bobo", 2)
est 0
7) La recherche dans une chaîne de caractères :
a) char * strchr(char * chaine, char unCar)
valeur retournée par la fonction
un pointeur NULL
signification
unCar ne fait pas
partie de la chaîne
un pointeur non NULL
qui pointe vers le 1er
caractère trouvé à partir
du début de la chaîne
unCar fait partie
de la chaîne
Exemple :
...
do
{ printf("Tapez votre choix parmi les options A, T ou Q ");
fflush(stdin);
choix = getchar();
if (strchr("ATQ", choix) == NULL) printf("choix invalide\n");
}
while ( strchr("ATQ", choix) == NULL );
...
Avec
char * P ;
et
P = strchr("Bonsoir", 'o');
on a :
| P (non NULL)
|
v
| 'B' | 'o' | 'n' | 's' | 'o' | 'i' | 'r' |'\0'|
|_____|_____|_____|_____|_____|_____|_____|____|
b) char * strrchr(char * chaine,char
unCar)
Elle fonctionne comme strchr en examinant la chaîne à partir
de la fin :
Avec
char * P ;
et
P = strrchr("Bonsoir", 'o');
Chapitre 4 : Fichiers de type texte
Page 88
on a :
| P (non NULL)
|
v
| 'B' | 'o' | 'n' | 's' | 'o' | 'i' | 'r' |'\0'|
|_____|_____|_____|_____|_____|_____|_____|____|
c) char * strstr(char * chaine, char * sousChaine)
Elle recherche dans la chaîne, la première occurrence complète
de la sous chaîne.
Exemple :
Avec
char * P ;
et
P = strstr("Bonsoir", "so");
on a :
| P (non NULL)
|
v
| 'B' | 'o' | 'n' | 's' | 'o' | 'i' | 'r' |'\0'|
|_____|_____|_____|_____|_____|_____|_____|____|
8) La conversion :
a) MAJUSCULE et minuscule :
strupr (chaine); ==> la chaîne sera en majuscule
strlwr (chaine); ==> la chaîne sera en minuscule
b) chaîne de caractères en valeur numérique :
int
long
double
atoi (char * chaine)
atol (char * chaine)
atof (char * chaine)
: chaîne en entier
: chaîne en "long" entier
: chaîne en double (grand réel)
c) valeur numérique en chaîne de caractères :
char * itoa(int n, char * chaine, int base) :
un entier dans une base (2 à 36 dont 10 est la base
décimale) en chaîne de caractères
Chapitre 4 : Fichiers de type texte
Page 89
char * ltoa(long n, char * chaine, int base) :
un long entier dans une base (2 à 36 dont 10 est la
base décimale) en chaîne de caractères
9) La lecture dans un fichier texte :
Exemple d'un fichier de données de type texte :
LONGPRE LISE
CHARTRAND ANDRE
etc ...
1.67
1.72
58.6
75.4
La lecture :
#define LONG_NP 31
char
nomPre[LONG_NP] ; /* y compris le caractère '\0' */
.....
while (!feof(donnees))
{
fgets (nomPre, LONG_NP, donnees); /* dans <string.h> */
fscanf(donnees,"%f%f\n", &Taille, &Poids);
etc ...
}
10) Traitement d'une chaîne par soi-même :
1. Arithmétique des pointeurs :
a. Soient les déclarations suivantes :
int t[4] = { 5, 10, 15, 20 }, * P = t ;
P est un pointeur vers t[0] (car t vaut &t[0]) :
P
|
Adresse (2 octets pour
└----------> ╔═══════════════╗
178
mémoriser 1 entier)
t[0]
║
5
║
179
╔═══════════════╗
180
t[1]
║
10
║
181
╔═══════════════╗
182
t[2]
║
15
║
183
╔═══════════════╗
184
t[3]
║
20
║
185
╔═══════════════╗
Chapitre 4 : Fichiers de type texte
Page 90
En C, P + 2 par exemple a un sens, c'est l'adresse de t[2]
(182 sur le schéma) :
P + 2 =
P + 2 * (sizeof(*P))
(*P est de type int)
= 178 + 2 * 2
= 182
(c'est l'adresse de t[2])
Ainsi :
P++ ;
qui est équivalent à P = P + 1 ;
fait pointer le pointeur P vers l'élément suivant (t[1])
b. Soient les instructions suivantes :
char * ch = "Bonsoir" , * P = ch ;
P ----->
| 'B' | 'o' | 'n' | 's' | 'o' | 'i' | 'r' |'\0'|
|_____|_____|_____|_____|_____|_____|_____|____|
L'instruction :
if (*P++) printf("%c", *P);
fait afficher la lettre 'o' à l'écran et fait avancer le
pointeur P vers le caractère 'o' :
Notez que
if ( *P++ ) <===>
if (*P != '\0' , P++)
Comme *P est 'B' qui n'est pas '\0', la condition est vraie
et P++ fait pointer P vers 'o'.
P
| 'B' | 'o' | 'n' | 's' | 'o' | 'i' | 'r' |'\0'|
|
|_____|_____|_____|_____|_____|_____|_____|____|
|
^
|
|
└---------------------┘
Ainsi, printf("%c", *P) ; fait afficher le caractère
pointé par P, c'est la lettre 'o'.
Chapitre 4 : Fichiers de type texte
Page 91
2. Parcourir tous les caractères d'une chaîne :
a.
On utilise souvent la longueur et les indices :
char souhaite[30] = "Bonne chance!";
/* Ou char * souhaite = "Bonne chance!" ; */
int longueur = strlen(souhaite), i ;
for ( i = 0 ; i < longueur ; i++)
printf("%c", souhaite[i]);
b.
On utilise la notion de pointeur :
char * urgent = "911" , * P ;
P = urgent ;
while (*P) printf("%c", *P++);
Notez que la valeur de *P est le caractère pointé par P
et que (*P) est équivalent à (*P != '\0').
De plus, P++ permet de faire pointer P vers le prochaine
caractère.
Au début :
P ----->
| '9' | '1' | '1' |'\0'|
|_____|_____|_____|____|
*P
Comme *P vaut '9' qui est différent de '\0', on affiche
la valeur de *P, c'est 9 et on fait pointer P vers
le prochaine caractère :
P
────────┐
v
| '9' | '1' | '1' |'\0'|
|_____|_____|_____|____|
*P
Chapitre 4 : Fichiers de type texte
Page 92
Comme *P vaut '1' qui est différent de '\0', on affiche
la valeur de *P, c'est 1 et on fait pointer P vers
le prochaine caractère :
P
───────────────┐
v
| '9' | '1' | '1' |'\0'|
|_____|_____|_____|____|
*P
etc ....
3. Déterminer la longueur d'une chaîne par soi-même :
int longueur ( const char * chaine )
{
int N = 0 ;
while (*chaine++) N++;
return N;
}
Exercice :
Simuler la fonction avec la chaîne : "Bonsoir".
11) Exemples et exercices :
Exemple 1 :
Écrire un bloc d'énoncés permettant d'afficher les 7 journées de
la semaine comme suit :
Journée 1
Journée 2
etc ....
:
:
dimanche
lundi
Solution :
int rang ;
char * jours[7] = { "dimanche", "lundi", "mardi", "mercredi",
"jeudi", "vendredi", "samedi" };
for ( rang = 0 ; rang < 7 ; rang++)
printf("Journée %d
Chapitre 4 : Fichiers de type texte
:
%s\n", rang+1, jours[rang]);
Page 93
Exemple 2 :
Écrire un programme permettant de saisir une chaîne de caractères
tapés au clavier, d'afficher la chaîne, de vérifier et d'afficher
si la chaîne est un palindrôme (même lecture dans deux sens :
exemple "Laval").
Solution :
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<alloc.h>
<string.h>
int palindrome ( char chaine[] )
{ int i , j , n = strlen(chaine);
strupr(chaine); /* convertir en MAJUSCULE */
for ( i = 0, j = n - 1 ; i < j ; i++, j-- )
if ( chaine[i] != chaine[j] ) return 0 ;
return 1 ;
}
void main ()
{ char * ch ;
ch = (char *) malloc(90) ; /* allouer la mémoire */
printf("Tapez une chaîne de caractères ");
gets(ch);
printf("La chaine %s %s\n", ch, palindrome(ch) ?
" est un palindrôme" : " n'est pas un palindrôme " );
}
Exercice 1 :
Soient les instructions suivantes :
char * ch = "Bonsoir" , * P = ch ;
int N = 0 ;
Quel est le rôle de N dans le bloc d'instructions suivantes ? :
while (*P)
if ( strchr("AEIOUY", toupper(*P++)) ) N++ ;
Chapitre 4 : Fichiers de type texte
Page 94
Exemple 3 :
Écrire un programme qui permet de saisir le nom d'un verbe régulier
du premier groupe (ex. chanter, aimer, ...). Le programme conjugue le
verbe au présent de l'indicatif.
Solution :
#include
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<alloc.h>
<string.h>
<ctype.h>
void obtenir( char * verbe )
/* verbe régulier (pas manger ==> nous mangeons à la place
de nous mangeons),
premier groupe (se termine par er (comme aimer)
*/
{
char * P ;
do
{ printf("Entrez le nom d'un verbe régulier du 1er groupe "):
gets(verbe);
P = strstr(verbe, "er") ;
if (!P) printf("%s n'est pas du premier groupe\n", verbe);
}
while (!P);
}
void conjuger(char * verbe)
{ char * base ;
int i, k ;
k = strlen(verbe);
base = (char *) malloc(k-2);
for ( i = 0 ; i < k-2 ; i++ )
base[i] = verbe[i];
base[k-2] = '\0';
printf("\n\nPrésent de l'indicatif du verbe %s\n", verbe);
if (strchr("AEIOUY", toupper(*verbe)))
printf("J' ");
else
printf("Je ");
printf("%se\n", base);
printf("Tu %ses\n", base);
printf("Il %se\n", base);
printf("Nous %sons\n", base);
printf("Vous %sez\n", base);
printf("Ils %sent\n", base);
}
Chapitre 4 : Fichiers de type texte
Page 95
void main()
{ char * verbe ;
do
{
verbe = (char *) malloc(80);
obtenir(verbe);
conjuger (verbe);
printf("Avez-vous un autre verbe à conjuger ? (O/N) ");
fflush(stdin);
}
while ( toupper(getchar()) == 'O');
}
Exécution :
Entrez le nom du verbe régulier du 1er groupe
chanter
Présent de l'indicatif du verbe chanter
Je chante
Tu chantes
Il chante
Nous chantons
Vous chantez
Ils chantent
Avez-vous un autre verbe à conjuger ? (O/N) o
Entrez le nom d'un verbe régulier du 1er groupe
aimer
Présent de l'indicatif du verbe aimer
J' aime
Tu aimes
Il aime
Nous aimons
Vous aimez
Ils aiment
Avez-vous un autre verbe à conjuger ? (O/N) n
Exemple 4:
Écrire votre "propre fonction" qui joue le rôle de la fonction
"strcat" (concaténation de chaînes de caractères, page 87).
Chapitre 4 : Fichiers de type texte
Page 96
Solution :
char * concat ( char * destination, const char * source )
{
char * P = destination + strlen(destination) ;
while (*source) *P++ = *source++ ;
*P = '\0';
return destination ;
}
Notes :
L'instruction :
while (*source) *P++ = *source++ ;
peut s'écrire plus court encore comme suit :
while (*P++ = *source++) ;
dont l'expression entre ( et ) est évaluée comme suit :
P = source ;
/* une affectation */
*P est-il différent de '\0'
source++ ;
P++ ;
(condition sous while)
/* vers le caractère suivant */
/* vers le caractère suivant */
Une version plus "compréhensible" de cette fonction est :
char * concat ( char * destination, const char * source )
{
char * P = destination ;
while (*P != '\0') P++ ;
while (*source != '\0')
{
*P = *source ;
P++ ;
source++;
}
*P = '\0';
return destination ;
}
Chapitre 4 : Fichiers de type texte
Page 97
Exercice 2 :
Écrire vos "propres fonctions" qui jouent le rôle de :
1. la fonction
"strcpy" (copie de chaînes de caractères)
2. la fonction "strstr" (une chaîne est-elle une sous-chaîne
d'une autre chaîne ?)
3. la fonction
minuscules).
Chapitre 4 : Fichiers de type texte
"strlwr" (conversion toute la chaîne en
Page 98
Téléchargement