« Exercices en langage C/Fonctions » : différence entre les versions

Aller à la navigation Aller à la recherche
→‎Lire une ligne longue avec fgets : Solution et remarques
(→‎Lire une ligne longue avec fgets : Solution et remarques)
==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.
 
* 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 =
<source lang="c">
* Compilation . : gcc -pedantic -Wall -std=c99 -c lire_ligne.c
* 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>
</pre>
}} </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.
227

modifications

Menu de navigation