« Exercices en langage C/Fonctions » : différence entre les versions
Contenu supprimé Contenu ajouté
→Lire une ligne longue avec fgets : Suite ennoncé |
→Lire une ligne longue avec fgets : Solution et remarques |
||
Ligne 8 : | Ligne 8 : | ||
==Lire une ligne longue avec fgets== |
==Lire une ligne longue avec fgets== |
||
===Problème à résoudre=== |
|||
La fonction <code>fgets</code> de la bibliothèque standard du langage C permet de lire une chaine de caractère de longueur limitée dans un flux. |
La fonction <code>fgets</code> de la bibliothèque standard du langage C permet de lire une chaine de caractère de longueur limitée dans un flux. |
||
Ligne 21 : | Ligne 22 : | ||
* Les instructions de compilation et d'édition de lien sont dans les commentaires des fichiers fournis. |
* Les instructions de compilation et d'édition de lien sont dans les commentaires des fichiers fournis. |
||
===Éléments fournis=== |
|||
<div style="width:70%">{{Boîte déroulante|titre=lire_ligne.c à compléter|contenu = |
<div style="width:70%">{{Boîte déroulante|titre=lire_ligne.c à compléter|contenu = |
||
<source lang="c"> |
<source lang="c"> |
||
Ligne 64 : | Ligne 66 : | ||
* Compilation . : gcc -pedantic -Wall -std=c99 -c lire_ligne.c |
* Compilation . : gcc -pedantic -Wall -std=c99 -c lire_ligne.c |
||
* Fichiers lies : main_lire_ligne.c, lire_ligne.h |
* Fichiers lies : main_lire_ligne.c, lire_ligne.h |
||
* |
|||
* Remarque : |
|||
* Beaucoup de commentaires sont présents pour un usage didactique. |
|||
* Ce programme utilise une syntaxe specifique C99. |
|||
* Ce programme a ete controle avec l'outil de verification statique |
|||
* Splint v3.1.2 du 19 Jan 2008 : http://www.splint.org/ |
|||
*/ |
*/ |
||
#include <stdio.h> |
#include <stdio.h> |
||
Ligne 230 : | Ligne 226 : | ||
</pre> |
</pre> |
||
}} </div> |
}} </div> |
||
===Solution proposées=== |
|||
<div style="width:70%">{{Boîte déroulante|titre=Voir la solution|contenu = |
|||
<source lang="c"> |
|||
/** |
|||
* Fonction .. : lire_ligne |
|||
* Role ...... : tente de lire une ligne entiere depuis le flux. |
|||
* - Le caractere de saut de ligne '\n' final est enleve. |
|||
* - L'appellant doit se charger de la desallocation de la chaine retournee. |
|||
* |
|||
* Parametres : |
|||
* - pChaine : adresse de retour de la chaine |
|||
* - tailleBufferLecture : taille du buffer de lecture, |
|||
* dans la pratique on pourra choisir _POSIX_MAX_INPUT (ref limits.h). |
|||
* - flux : un pointeur sur le descripteur du fichier a lire. |
|||
* |
|||
* - Valeur retournee : |
|||
* - Si OK : EXIT_SUCCESS. |
|||
* - Si Fin de fichier atteinte sans rencontrer \n : EOF, |
|||
* - la chaine retournée contient le debut de la ligne longue. |
|||
* - Si rien a lire, retour d'un pointeur NULL pour la chaine. |
|||
* - En cas de probleme : Le code d'erreur systeme utilisable par perror. |
|||
* et retour d'un pointeur NULL pour la chaine. |
|||
* |
|||
* Auteur ...... : Thierry46 |
|||
* Version ..... : 1.0 du 30/1/2008 |
|||
* Licence ..... : GNU GPL. |
|||
lire_ligne : Lecture d'une ligne longue dans un fichier texte |
|||
Copyright (C) 2008 Thierry46 |
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
* Compilation . : gcc -pedantic -Wall -std=c99 -c lire_ligne.c |
|||
* Fichiers lies : main_lire_ligne.c, lire_ligne.h |
|||
* |
|||
* Remarques : |
|||
* Beaucoup de commentaires sont présents pour un usage didactique. |
|||
* Ce programme utilise une syntaxe specifique C99. |
|||
* Ce programme a ete controle avec l'outil de verification statique |
|||
* Splint v3.1.2 du 19 Jan 2008 : <http://www.splint.org/> |
|||
*/ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdbool.h> |
|||
#include <assert.h> |
|||
#include <errno.h> |
|||
#include "lire_ligne.h" |
|||
/*@null@*/ char *lire_ligne(size_t tailleBufferLecture, |
|||
FILE * restrict flux, int *pCodeRetour) |
|||
{ |
|||
// Declarations |
|||
char *chaineRetour = NULL; |
|||
bool litEncore = true; |
|||
bool rienLu = true; |
|||
int codeRetour = EXIT_SUCCESS; |
|||
// Controle rapide des parametres. |
|||
// Si quelque chose ne va pas, cela ne sert à rien d'aller plus loin. |
|||
assert(tailleBufferLecture > 0); |
|||
assert(flux != NULL); |
|||
// Boucle de lecture d'une ligne eventuellement longue |
|||
do |
|||
{ |
|||
size_t positionEcriture; |
|||
litEncore = false; |
|||
// Si 1ere lecture : demande d'une nouvelle zone memoire |
|||
if (chaineRetour == NULL) |
|||
{ |
|||
positionEcriture = 0; |
|||
chaineRetour = malloc(tailleBufferLecture); |
|||
if (chaineRetour == NULL) |
|||
{ |
|||
codeRetour = errno; |
|||
} |
|||
} |
|||
else // Pour les lectures suivantes : extension de la zone memoire |
|||
{ |
|||
char *nouvelleChaine; |
|||
positionEcriture = strlen(chaineRetour); |
|||
nouvelleChaine = realloc(chaineRetour, positionEcriture + tailleBufferLecture); |
|||
if (nouvelleChaine == NULL) |
|||
{ |
|||
codeRetour = errno; |
|||
free(chaineRetour); |
|||
chaineRetour = NULL; |
|||
} |
|||
else |
|||
{ |
|||
chaineRetour = nouvelleChaine; |
|||
} |
|||
} // if (chaineRetour == NULL) |
|||
// Si le systeme a accorde la memoire, lecture d'une chaine par fgets |
|||
if (chaineRetour != NULL) |
|||
{ |
|||
if (fgets(chaineRetour+positionEcriture, (int)tailleBufferLecture, flux) |
|||
!= NULL) |
|||
{ |
|||
// Recherche si un \n a ete lu en fin de chaine. |
|||
char *positionNewLine = strrchr(chaineRetour, '\n'); |
|||
rienLu = false; |
|||
if (positionNewLine != NULL) |
|||
{ |
|||
// Suppression du caractere de fin de ligne \n, |
|||
*positionNewLine = '\0'; |
|||
} |
|||
else // fgets n'a pas pu lire la ligne complete. |
|||
{ |
|||
litEncore = true; |
|||
} |
|||
} |
|||
else if (ferror(flux) != 0) |
|||
{ |
|||
codeRetour = errno; |
|||
free(chaineRetour); |
|||
chaineRetour = NULL; |
|||
} else if (feof(flux) != 0) |
|||
{ |
|||
codeRetour = EOF; |
|||
if (rienLu) |
|||
{ |
|||
free(chaineRetour); |
|||
chaineRetour = NULL; |
|||
} |
|||
} |
|||
} // if (chaineRetour != NULL) |
|||
} while (litEncore); |
|||
// Retour des resultats. |
|||
*pCodeRetour = codeRetour; |
|||
return chaineRetour; |
|||
} // char *lire_ligne(... |
|||
</source> |
|||
}} </div> |
|||
===Remarques sur l'exercice=== |
|||
* Le test des paramètres dans la solution est expéditif. |
|||
* Pour obtenir des programmes robustes, le langage C oblige à une gestion pénible des erreurs. Avec le langage Java par exemple, les mécanismes d'exception facilitent la tâche du programmeurs. |
|||
* L'utilisation de l'allocation dynamique de mémoire est risquée : fuite mémoire. Avec le langage Java par exemple, le ramasse miettes (Garbage collector) facilite la tâche du programmeurs. |
|||
* En Java des classes comme String et StringBuffer prennent en charge les chaines de caractères longues. |
Version du 2 février 2008 à 08:21
Les fonctions
Ces exercices concernent l'utilisation des fonctions de la bibliothèque standard du langage C.
Lire une ligne longue avec fgets
Problème à résoudre
La fonction fgets
de la bibliothèque standard du langage C permet de lire une chaine de caractère de longueur limitée dans un flux.
Vous allez compléter une fonction lire_ligne répondant au spécifications suivantes :
- Retour d'une ligne lue dans un flux texte passé en paramètre.
- Vous éliminerez les caractères de saut de ligne lus.
- La longueur des lignes lues n'est pas limitée.
- Contrôle des paramètres et retour des codes d'erreurs systèmes, détection de la fin du fichier.
- Vous utiliserez au maximum les fonctions de la bibliothèque standard du langage C : allocation mémoire, chaines de caractères...
- Son prototype est donné par lire_ligne.h.
- Vous utiliserez le programme de main_lire_ligne.c pour lire_ligne.
- Vous devrez traiter le fichier test_lire_ligne.txt fourni.
- Les instructions de compilation et d'édition de lien sont dans les commentaires des fichiers fournis.
Éléments fournis
lire_ligne.c à compléter
/**
* Fonction .. : lire_ligne
* Role ...... : tente de lire une ligne entiere depuis le flux.
* - Le caractere de saut de ligne '\n' final est enleve.
* - L'appellant doit se charger de la desallocation de la chaine retournee.
*
* Parametres :
* - pChaine : adresse de retour de la chaine
* - tailleBufferLecture : taille du buffer de lecture,
* dans la pratique on pourra choisir _POSIX_MAX_INPUT (ref limits.h).
* - flux : un pointeur sur le descripteur du fichier a lire.
*
* - Valeur retournee :
* - Si OK : EXIT_SUCCESS.
* - Si Fin de fichier atteinte sans rencontrer \n : EOF,
* - la chaine retournée contient le debut de la ligne longue.
* - Si rien a lire, retour d'un pointeur NULL pour la chaine.
* - En cas de probleme : Le code d'erreur systeme utilisable par perror.
* et retour d'un pointeur NULL pour la chaine.
*
* Auteur ...... : Votre nom
* Version ..... : 1.0 du date
* Licence ..... : GNU GPL.
lire_ligne : Lecture d'une ligne longue dans un fichier texte
Copyright (C) 2008 Thierry46
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Compilation . : gcc -pedantic -Wall -std=c99 -c lire_ligne.c
* Fichiers lies : main_lire_ligne.c, lire_ligne.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <errno.h>
#include "lire_ligne.h"
/*@null@*/ char *lire_ligne(size_t tailleBufferLecture,
FILE * restrict flux, int *pCodeRetour)
{
// Début partie à compléter...
// Fin partie à compléter...
} // char *lire_ligne(...
lire_ligne.h
/*
* Nom .... : lire_ligne.h
* Role ... : Definitions et prototype de lire_ligne.h
* Auteur ...... : Thierry46
* Version ..... : 1.0 du 30/1/2008
* Licence ..... : GNU GPL.
lire_ligne : Lecture d'une ligne longue dans un fichier texte
Copyright (C) 2008 Thierry46
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Fichier lie : lire_ligne.c
*/
#ifndef LIRE_LIGNE_H
#define LIRE_LIGNE_H
extern /*@null@*/ char *lire_ligne(size_t tailleBufferLecture,
FILE * restrict flux, int *pCodeRetour);
#endif
main_lire_ligne.c
/*
* Nom ......... : main_lire_ligne.c
* Role ........ : Tester la fonction lire_ligne
* Parametre ... : Aucun
* Code retour . : EXIT_SUCESS (0)
* Auteur ...... : Thierry46
* Version ..... : 1.0 du 30/1/2008
* Licence ..... : GNU GPL.
lire_ligne : Lecture d'une ligne longue dans un fichier texte
Copyright (C) 2008 Thierry46
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Compilation . :
* Voir lire_ligne .c pour sa compilation
* gcc -pedantic -Wall -std=c99 -o test_lire_ligne.exe main_lire_ligne.c lire_ligne.o
* Exécution : ./test_lire_ligne.exe
* Fichiers lies : lire_ligne.c, lire_ligne.h, test_lire_ligne.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lire_ligne.h"
#define FIC_TEST "test_lire_ligne.txt"
// Valeur faible de TAILLE_BUFFER pour test débordements et lecture multiple.
#define TAILLE_BUFFER (size_t)10
#define TAILLE_MAX_MESSAGE 256
int main(void)
{
FILE *hFicTest = NULL;
char *chaine = NULL;
int numLigne = 1;
int retour = EXIT_SUCCESS;
char message[TAILLE_MAX_MESSAGE];
hFicTest = fopen(FIC_TEST, "r");
if (hFicTest == NULL)
{
(void)printf("%s : le fichier %s n'a pas pu etre ouvert !\n",
__func__, FIC_TEST);
// Je laisse poursuivre pour tester assertion dans lire_ligne. //
}
do
{
chaine = lire_ligne(TAILLE_BUFFER, hFicTest, &retour);
if (retour == EXIT_SUCCESS)
{
(void)strncpy(message, "Pas d'erreur", sizeof(message) - 1);
}
else if (retour == EOF)
{
(void)strncpy(message, "Fin de fichier", sizeof(message) - 1);
}
else
{
(void)strncpy(message, strerror(retour), sizeof(message) - 1);
}
message[sizeof(message) - 1] = '\0';
(void)printf("Ligne %d : retour = %d (%s), chaine lue = \"%s\".\n",
numLigne, retour, message,
(chaine == NULL) ? "! Pointeur NULL !" : chaine);
if (chaine != NULL)
{
free(chaine);
chaine = NULL;
}
} while (retour == EXIT_SUCCESS);
if (hFicTest != NULL)
{
(void)fclose(hFicTest);
}
return EXIT_SUCCESS;
} // int main(void)
test test_lire_ligne.txt
1 1234567880 ligne longue. 12345678901234567890123456789012345678901234567890 fin
Solution proposées
Voir la solution
/**
* Fonction .. : lire_ligne
* Role ...... : tente de lire une ligne entiere depuis le flux.
* - Le caractere de saut de ligne '\n' final est enleve.
* - L'appellant doit se charger de la desallocation de la chaine retournee.
*
* Parametres :
* - pChaine : adresse de retour de la chaine
* - tailleBufferLecture : taille du buffer de lecture,
* dans la pratique on pourra choisir _POSIX_MAX_INPUT (ref limits.h).
* - flux : un pointeur sur le descripteur du fichier a lire.
*
* - Valeur retournee :
* - Si OK : EXIT_SUCCESS.
* - Si Fin de fichier atteinte sans rencontrer \n : EOF,
* - la chaine retournée contient le debut de la ligne longue.
* - Si rien a lire, retour d'un pointeur NULL pour la chaine.
* - En cas de probleme : Le code d'erreur systeme utilisable par perror.
* et retour d'un pointeur NULL pour la chaine.
*
* Auteur ...... : Thierry46
* Version ..... : 1.0 du 30/1/2008
* Licence ..... : GNU GPL.
lire_ligne : Lecture d'une ligne longue dans un fichier texte
Copyright (C) 2008 Thierry46
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Compilation . : gcc -pedantic -Wall -std=c99 -c lire_ligne.c
* Fichiers lies : main_lire_ligne.c, lire_ligne.h
*
* Remarques :
* Beaucoup de commentaires sont présents pour un usage didactique.
* Ce programme utilise une syntaxe specifique C99.
* Ce programme a ete controle avec l'outil de verification statique
* Splint v3.1.2 du 19 Jan 2008 : <http://www.splint.org/>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <errno.h>
#include "lire_ligne.h"
/*@null@*/ char *lire_ligne(size_t tailleBufferLecture,
FILE * restrict flux, int *pCodeRetour)
{
// Declarations
char *chaineRetour = NULL;
bool litEncore = true;
bool rienLu = true;
int codeRetour = EXIT_SUCCESS;
// Controle rapide des parametres.
// Si quelque chose ne va pas, cela ne sert à rien d'aller plus loin.
assert(tailleBufferLecture > 0);
assert(flux != NULL);
// Boucle de lecture d'une ligne eventuellement longue
do
{
size_t positionEcriture;
litEncore = false;
// Si 1ere lecture : demande d'une nouvelle zone memoire
if (chaineRetour == NULL)
{
positionEcriture = 0;
chaineRetour = malloc(tailleBufferLecture);
if (chaineRetour == NULL)
{
codeRetour = errno;
}
}
else // Pour les lectures suivantes : extension de la zone memoire
{
char *nouvelleChaine;
positionEcriture = strlen(chaineRetour);
nouvelleChaine = realloc(chaineRetour, positionEcriture + tailleBufferLecture);
if (nouvelleChaine == NULL)
{
codeRetour = errno;
free(chaineRetour);
chaineRetour = NULL;
}
else
{
chaineRetour = nouvelleChaine;
}
} // if (chaineRetour == NULL)
// Si le systeme a accorde la memoire, lecture d'une chaine par fgets
if (chaineRetour != NULL)
{
if (fgets(chaineRetour+positionEcriture, (int)tailleBufferLecture, flux)
!= NULL)
{
// Recherche si un \n a ete lu en fin de chaine.
char *positionNewLine = strrchr(chaineRetour, '\n');
rienLu = false;
if (positionNewLine != NULL)
{
// Suppression du caractere de fin de ligne \n,
*positionNewLine = '\0';
}
else // fgets n'a pas pu lire la ligne complete.
{
litEncore = true;
}
}
else if (ferror(flux) != 0)
{
codeRetour = errno;
free(chaineRetour);
chaineRetour = NULL;
} else if (feof(flux) != 0)
{
codeRetour = EOF;
if (rienLu)
{
free(chaineRetour);
chaineRetour = NULL;
}
}
} // if (chaineRetour != NULL)
} while (litEncore);
// Retour des resultats.
*pCodeRetour = codeRetour;
return chaineRetour;
} // char *lire_ligne(...
Remarques sur l'exercice
- Le test des paramètres dans la solution est expéditif.
- Pour obtenir des programmes robustes, le langage C oblige à une gestion pénible des erreurs. Avec le langage Java par exemple, les mécanismes d'exception facilitent la tâche du programmeurs.
- L'utilisation de l'allocation dynamique de mémoire est risquée : fuite mémoire. Avec le langage Java par exemple, le ramasse miettes (Garbage collector) facilite la tâche du programmeurs.
- En Java des classes comme String et StringBuffer prennent en charge les chaines de caractères longues.