IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

reallocation de buffer de lecture de fichier


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Par défaut reallocation de buffer de lecture de fichier
    Bonjour

    Ça fait maintenant un bon bout de temps que je ne fréquente plus le forum C.
    Je viens de recevoir un mp au sujet d'une contribution que j'avais écrite sur la réallocation du buffer de saisie à la volée.
    Ne l'ayant plus et ne la trouvant pas sur le site, j'ai tenté de la réécrire et je viens la poster ici pour deux raisons :
    • savoir si elle est correcte
    • que le po ne s'approprie pas la réponse et que tout le monde puise la partager (si elle est correcte bien entendu)

    Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // fonction de lecture de lignes d'un fichier ouver en mode texte
    int lecture(FILE *f)
    {
    	// longueur initiale du buffer de lecture
    	// on peut choisir une autre valeur bien entendu !
    	size_t len = 10;
    	size_t old_len = len;
    	// le meilleur facteur d'accroissement de longueur est le nombre d'or !
    	// le nombre d'or = 1,61803399
    	double incr = 1.62;
    	char *ptr;
    	int flag = 0 ; // 0 si la lecture a été complète, 1 sinon
     
    	// initialisation du buffer de saisie
    	ptr = malloc(len);
     
    	// toujours tester le retour du malloc
    	if (ptr == NULL)
    	{
    		fprintf(stderr, "Pb alloc memoire \n");
    		return 0;
    	}
     
    	while(fgets(ptr + (flag > 0) * old_len - flag , len - (flag  > 0) * old_len, f) != NULL)
    	{
    		// on teste si la saisie est complète
    		char *p = strchr(ptr, '\n') ;
    		if (p == NULL)
    		{
    			// saisie incomplète
    			char *tmp ;
     
    			old_len = len;
    			len = (size_t) (incr * len);
     
    			tmp = realloc(ptr, len);
    			if (tmp == NULL)
    			{
    				// l'allocation a echoue, on ne peut rien faire
    				fprintf(stderr, "Pb alloc memoire \n");
    				return 0;
    			}
    			else
    				ptr = tmp;
    			flag ++;
    		}
    		else
    		{
    			*p = 0;
    			flag = 0;
     
    			// ici on peut travaille sur la saisie complète
    			// simple affichage 
     		        puts(ptr);
    		}
    	}
    	free(ptr);
    	return 1;
    }
    J'avoue que j'ai un problème sur le fgets, je ne sais pas bien expliquer pourquoi il faut écrire (ptr + (flag > 0) * old_len - flag , si on passe plusieurs fois dans une lecture incomplète, flag comptant le nombre de lecture incomplètes consécutives.

    merci de vos remarques éventuelles.
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

  2. #2
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    En fait, il s'agit certainement d'une erreur sur la taille du buffer disponible qui a été compensée par le -flag dans ptr + (flag > 0) * old_len - flag

    - Au début d'une nouvelle ligne, le fgets est équivalent à ce qui est correct.

    - Si cette ligne est incomplète, on a un '\0' en position ptr+oldlen-1.
    le fgets() devient (avec flag=1)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fgets(ptr + old_len - 1, len - old_len, f)
    Le -1 est destiné à se positionner sur le dernier élément du buffer précédent pour écraser le '\0' qu'il contient.
    Le buffer disponible est alors de taille len - old_len+1 (à cause de la récupération de la place du '\0') mais on ne demande bizarrement que len - old_len caractères

    - Si la ligne est à nouveau incomplète, le '\0' va alors se trouver en ptr + old_len - 1 + len - old_len -1 = ptr+len-2. Ce qui explique le fgets suivant (avec flag =2)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fgets(ptr + old_len - 2, len - old_len, f)
    Avec toujours cette étrangeté sur la longueur disponible dans le buffer qui en réalité est maintenant len - old_len+2

    Ceci étant, j'avoue que je n'aime pas ce genre d'expressions (flag > 0) * old_len qui mélange des type booléens et arithmétiques : pour moi flag>0 vaut vrai ou faux et je ne veux pas avoir à considérer que vrai est 1 et faux 0. Je préfère ptr - flag+ (flag > 0) ? old_len : 0

  3. #3
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Par défaut
    Merci de ta réponse, j'étais focalisé sur le positionnement du début de lecture et j'ai oublié en cours la longueur à lire qui ne me paraissait pas cohérente.
    Merci de tes remarques, je corrige :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // fonction de lecture de lignes d'un fichier ouver en mode texte
    int lecture(FILE *f)
    {
    	// longueur initiale du buffer de lecture
    	// on peut choisir une autre valeur bien entendu !
    	size_t len = 10;
    	size_t old_len = len;
    	// le meilleur facteur d'accroissement de longueur est le nombre d'or !
    	// le nombre d'or = 1,61803399
    	double incr = 1.62;
    	char *ptr;
    	int flag = 0 ; // 0 si la lecture a été complète, 1 sinon
     
    	// initialisation du buffer de saisie
    	ptr = malloc(len);
     
    	// toujours tester le retour du malloc
    	if (ptr == NULL)
    	{
    		fprintf(stderr, "Pb alloc memoire \n");
    		return 0;
    	}
     
    	while(fgets(ptr + ((flag > 0) ? old_len : 0) -flag, len - ((flag > 0) ? old_len : 0) + flag, f) != NULL)
    	{
    		// on teste si la saisie est complète
    		char *p = strchr(ptr, '\n') ;
    		if (p == NULL)
    		{
    			// saisie incomplète
    			char *tmp ;
     
    			old_len = len;
    			len = (size_t) (incr * len);
     
    			tmp = realloc(ptr, len);
    			if (tmp == NULL)
    			{
    				// l'allocation a echoue, on ne peut rien faire
    				fprintf(stderr, "Pb alloc memoire \n");
    				return 0;
    			}
    			else
    				ptr = tmp;
    			flag ++;
    		}
    		else
    		{
    			*p = 0;
    			flag = 0;
     
    			// ici on peut travaille sur la saisie complète
    			// simple affichage 
     		        puts(ptr);
    		}
    	}
    	free(ptr);
    	return 1;
    }
    Toute petite erreur, lorsque flag = 0, c'est équivalent à
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

  4. #4
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Le code est incorrect à cause d'une erreur sur l'adresse où on écrit.

    - Pouquoi ne pas utiliser
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    while(fgets(ptr + ((flag > 0) ? old_len-1 : 0 ), len - ((flag > 0) ? old_len-1: 0), f) != NULL)
    ....
    qui corrige l'erreur sur la place disponible dans le buffer ?
    Ou même carrément supprimer flag en utilisant old_len initialisé à 1 avant chaque ligne

    - Le strchr() peut ne se faire que sur la partie nouvellement lue. Il est inutile de faire analyser la partie déjà remplie du buffer.

    - Avec ces remarques :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    int lecture(FILE *f)
    {
      // longueur initiale du buffer de lecture
      // on peut choisir une autre valeur bien entendu !
      size_t len = 4;
      // le meilleur facteur d'accroissement de longueur est le nombre d'or !
      // le nombre d'or = 1,61803399
      double const incr = 1.62;
      char *ptr;
      size_t old_len;
      // initialisation du buffer de saisie
      if(len<2)len = 2;       //  len doit être > 1
      ptr = malloc(len);
      if (ptr == NULL) // toujours tester le retour du malloc
      {
        fprintf(stderr, "Pb alloc memoire \n");
        return 0;
      }
      old_len = 1;
      while(fgets(ptr+old_len-1, len-old_len+1, f) != NULL)
      {
        char *p = strchr(ptr+old_len-1, '\n') ; // on teste si la saisie est complète
        if (p == NULL) // saisie incomplète
        {
          char *tmp ;
          old_len = len;
          len = incr * len;
          tmp = realloc(ptr, len);
          if (tmp == NULL)
          {
            // l'allocation a echoue, on ne peut rien faire
            fprintf(stderr, "Pb alloc memoire \n");
            return 0;
          }
          else ptr = tmp;
        }
        else
        {
          *p = 0;
          old_len = 1;
          // ici on peut travaille sur la saisie complète
          // simple affichage
          puts(ptr);
        }
      }
      free(ptr);
      return 1;
    }

    - La valeur de len n'est pas réinitialisé à chaque début de ligne, donc on repart avec l'allocation obtenue précédemment. Ce n'est pas en soi un inconvénient, mais si on modifie la fonction pour qu'elle renvoie la ligne lue (au lieu de prévoir un traitement interne à la fonction), cela deviendra ennuyeux.

  5. #5
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Par défaut
    Bonjour

    Il faut que je me remette sérieusement au C, je n'arrive même plus à tester correctement

    Pas bete du tout cette histoire de old_len initialisée à 1 ça simplifie beaucoup le code.

    La seconde remarque sur le strchr est judicieuse aussi.

    Merci, je pense qu'on peut mettre à la discussion !
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Lecture rapide fichier binaire & Buffer
    Par Eames38 dans le forum C++
    Réponses: 6
    Dernier message: 15/06/2011, 16h39
  2. fread, buffer de lecture, feof(fichier)
    Par nicodn02 dans le forum C
    Réponses: 14
    Dernier message: 31/03/2010, 00h55
  3. [AS400][Intranet][PC] Lecture de "fichiers" AS400
    Par lando dans le forum Autres SGBD
    Réponses: 4
    Dernier message: 16/07/2003, 11h11
  4. Lecture de fichier
    Par Watcha dans le forum x86 16-bits
    Réponses: 13
    Dernier message: 04/03/2003, 20h43
  5. Lecture de fichiers ".WAV"...
    Par 0x4e84 dans le forum Langage
    Réponses: 2
    Dernier message: 03/09/2002, 09h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo