Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercice] getline

Mois de février

    1 février 2011 à 19:02:06

    Bonsoir,

    Ce sujet est prévu pour acceuillir toute les discussions à propos de l'exercice getline dont le sujet est rappelé en secret.

    getline (3)



    Prérequis



    • Entrées / Sorties
    • Manipulations de fichiers.
    • Gestion des erreurs.
    • (Ré)allocation de mémoire.
    • Utilisation de fonctions.


    Énoncé



    La fonction 'getline` qui sert à récupérer une ligne - chaine de caractères se terminant par un '\n'. Cette fonction bien pratique est une extension de la bibliothèque standard fournie par la glibc. Elle n'existe donc pas sur Windows, par contre, elle existe sur les Unix-like (pensez donc à nommer votre fonction autrement).

    L'exercice suivant vous propose de réécrire cette fonction qui est assez intéressante puisqu'elle fait appel à vos connaissances en C plutôt qu'à d'autres choses.

    Le manuel de la fonction :
    man (3) getline : (fr), (en).

    La gestion des entrées sorties et des erreurs n'est pas forcément évidente, n'hésitez donc pas à faire le travail étape par étape et à approfondir les concepts.

    L'idée originale est de yoch


    Bonne Chance.
    • Partager sur Facebook
    • Partager sur Twitter
      1 février 2011 à 23:50:51

      Pas mal comme exo. :)
      Ton lien vers la page de man (en) est erronée.
      Voilà mon code, j'ai fait quelques adaptations, tout n'est pas marqué dans la page de man. :-°

      #define _POUET_SOURCE /* Ok je sors... */
      #include <stdio.h>
      #include <stdlib.h>
      
      #define SZ_TMP  64
      
      int zgetdelim(char ** lineptr, size_t * n, int delim, FILE * f) {
        char * tmp;
        size_t i;
        int c, real;
        
        if (lineptr == NULL || n == NULL || f == NULL)
          return -1;
        
        if (*lineptr == NULL || *n <= 0) {
          real = 1;
          *lineptr = malloc(sizeof **lineptr * SZ_TMP);
          if (*lineptr == NULL)
            return -1;
          *n = SZ_TMP;
        }
        else
          real = 0;
        
        i = 0;
        while (1) {
          c = fgetc(f);
          
          if (c == EOF)
            return -1;
          
          (*lineptr)[i++] = c;
          
          if (i >= *n) {
            real = 1;
            *n += SZ_TMP;
            tmp = realloc(*lineptr, *n);
            
            if (tmp == NULL)
              return -1;
            else
              *lineptr = tmp;
          }
          
          if (c == delim)
            break;
        }
        
        (*lineptr)[i] = '\0';
        
        if (real) {
          *n = i;
          /* Pas sûr que ça serve à quelque chose...
           * pas précisé dans la page de man */
          /* Juste histoire d'adapter la taille du buffer */
          tmp = realloc(*lineptr, i);
          if (tmp == NULL)
            return -1;
          else
            *lineptr = tmp;
        }
        
        return i;
      }
      
      int zgetline(char ** lineptr, size_t * n, FILE * f) {
        return zgetdelim(lineptr, n, '\n', f);
      }
      
      int main (void) {
        char * tmp;
        size_t n;
        int ret;
        
        tmp = NULL;
        
        ret = zgetline(&tmp, &n, stdin);
        if (ret == -1) {
          fprintf(stderr, "Erreur.\n");
          return EXIT_FAILURE;
        }
        printf("%d - %lu : %s\n", ret, n, tmp);
        
        return EXIT_SUCCESS;
      }
      
      • Partager sur Facebook
      • Partager sur Twitter
        2 février 2011 à 1:16:09

        Citation : Pouet_forever

        Voilà mon code, j'ai fait quelques adaptations, tout n'est pas marqué dans la page de man. :-°


        Ton code traite EOF comme une erreur. Or d'après le manuel, ça n'est pas le comportement de getline. Et ce n'est pas non plus le comportement que j'attendrais intuitivement d'une fonction getline.
        • Partager sur Facebook
        • Partager sur Twitter
          2 février 2011 à 11:09:01

          Intéressant cet exercice, dommage qu'il ne s'agisse pas d'une fonction standard ^^
          Mon implémentation :


          #include <stdio.h>
          #include <stdlib.h>
          #include <limits.h>
          
          
          
          long
          my_getline (char **line, size_t *n, FILE *file)
          {
                  size_t size = 0;
                  int c;
          
                  if (!line || !n || !file) {
                          return -1;
                  }
          
                  if (!line[0]) {
                          if (!(line[0] = malloc (sizeof **line * UCHAR_MAX))) {
                                  return -1;
                          }
          
                          *n = UCHAR_MAX;
                  }
                 
                  while ((c = getc (file)) != EOF) {
                          if (*n < size) {
                                  char *tmp;
          
                                  if ((*n <<= 1) < size) {
                                          *n = (size_t) -1;
                                          line[0][size] = '\0';
                                          return -1;
                                  }
          
                                  if (!(tmp = realloc (line[0], *n))) {
                                          *n >>= 1;
                                          line[0][size] = '\0';
                                          return -1;
                                  }
          
                                  line[0] = tmp;
                          }
          
                          line[0][size++] = c; 
                          if (c == '\n') {
                                  break;
                          }
                  }
          
                  line[0][size] = '\0';
                  return size;
          }
          
          
          int
          main (void)
          {
                  char *buffer;
                  size_t size;
                  long nb;
          
                  if ((nb = getline (&buffer, &size, stdin)) != -1) {
                          printf ("%ld caractères lu:\n", nb);
                          printf ("%s", buffer);
                  }
          
                  free (buffer);
                  return EXIT_SUCCESS;
          }
          



          EDIT: au fait Pouet_forever tu as oublié l'appel à free à la fin du main(note moi aussi mais j'ai corrigé :-° ).
          • Partager sur Facebook
          • Partager sur Twitter
            2 février 2011 à 13:03:51

            @Taurre et @Pouet : normal que vous initialisez pas size à 0 dans votre main() ?

            (Car si j'ai bien compris le man, si size est > 0, celà veut dire que le buffer est déjà alloué.)

            Sinon, j'essaierais de poster un code aussi, l'exercice est sympatique :) .
            • Partager sur Facebook
            • Partager sur Twitter
              2 février 2011 à 13:22:21

              Citation : Tosh


              @Taurre et @Pouet : normal que vous initialisez pas size à 0 dans votre main() ?

              (Car si j'ai bien compris le man, si size est > 0, celà veut dire que le buffer est déjà alloué.)



              En fait, le man précise que si le pointeur line est nul, la fonction ne prend pas en compte la valeur de n, on peut donc le laisser non initialisé dans ce cas ;)

              Citation : man getline


              If *lineptr is NULL, then getline() will allocate a buffer for storing
              the line, which should be freed by the user program. (In this case,
              the value in *n is ignored.
              )

              • Partager sur Facebook
              • Partager sur Twitter
                2 février 2011 à 13:26:50

                Citation : Tosh


                (Car si j'ai bien compris le man, si size est > 0, celà veut dire que le buffer est déjà alloué.)


                J'ai compris que c'était plus le test sur lineptr qui décidait si, oui ou non le buffer était déjà alloué.
                Je pense que la valeur de size, n'a aucune importance à l'entrée de la fonction.

                Sinon

                Citation : Marc Mongenet


                Ton code traite EOF comme une erreur. Or d'après le manuel, ça n'est pas le comportement de getline



                Citation : man


                Both functions return -1 on failure
                to read a line (including end of file condition)


                Je passe à coté de quelque chose?
                • Partager sur Facebook
                • Partager sur Twitter
                Zeste de Savoir, le site qui en a dans le citron !
                  2 février 2011 à 13:57:56

                  Citation : GurneyH


                  Citation : man


                  Both functions return -1 on failure
                  to read a line (including end of file condition)


                  Je passe à coté de quelque chose?


                  Oui, la doc n'est pas terrible... De http://ftp.parisc-linux.org/cgi-bin/ma [...] tml?3+getline : The buffer is null-terminated and includes the newline character, if one was found.
                  Je suppose (sans tester) que ça retourne -1 seulement si aucun caractère n'a pu être lu (donc EOF immédiat).
                  En tout cas j'ai testé le code de Pouet_forever avec le getline de mon système, et il ne retourne pas une erreur en cas d'EOF après des caractères.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    2 février 2011 à 13:58:41

                    Mon implémentation — pas très testée vu que mon système (GNU) fournit getline, et que j’utilise donc systématiquement l’implémentation de la glibc au lieu de la mienne (je ne l’ai écrite que pour pouvoir éventuellement compiler mes programmes sur un système sans getline, ce que concrètement je n’ai jamais besoin de faire).


                    #include <stdio.h>
                    #include <stdlib.h>
                    #include <errno.h>
                    
                    ssize_t
                    getdelim(char **, size_t *, int, FILE *);
                    
                    #define getline(l,n,f)  getdelim((l), (n), '\n', (f))
                    
                    #define BLOCK_SIZE 512
                    
                    ssize_t
                    getdelim(char **buffer, size_t *len, int delim, FILE *f)
                    {
                        int c;
                        ssize_t n;
                    
                        if ( ! buffer || ! len || ! f ) {
                            errno = EINVAL;
                            return -1;
                        }
                    
                        if ( ! *buffer )
                            *len = 0;
                    
                        c = n = 0;
                        while ( c != delim && (c = fgetc(f)) != EOF ) {
                            if ( n + 1 >= *len ) {
                                char *sp;
                    
                                if ( ! (sp = realloc(*buffer, *len + BLOCK_SIZE)) )
                                    return -1;
                    
                                *buffer = sp;
                                *len += BLOCK_SIZE;
                            }
                    
                            *(*buffer + n++) = c;
                        }
                    
                        if ( n > 0 )
                            *(*buffer + n) = '\0';
                    
                        return n > 0 ? : n : -1;
                    }
                    



                    Citation : GurneyH

                    Citation : man

                    Both functions return -1 on failure
                    to read a line (including end of file condition)



                    Je passe à coté de quelque chose?


                    D’après moi, il faut comprendre que getline returne -1 si la fin de fichier a empêché toute lecture (donc si EOF est le premier caractère à sortir du flux). Si getline a lu ne serait-ce qu’un seul caractère avant de tomber sur l’EOF, elle ne doit pas interpréter ça comme une erreur mais plutôt comme une condition normale de fin de ligne, même si le caractère de fin de ligne lui-même n’a pas été lu.

                    Du moins, c’est mon interprétation. Après il est vrai que la page de manuel n’est pas explicite, et je ne suis jamais allé vérifier ce que faisait exactement l’implémentation de la glibc dans ce cas de figure.
                    • Partager sur Facebook
                    • Partager sur Twitter
                      3 février 2011 à 10:19:27

                      Ma solution :

                      #include <stdio.h>
                      #include <stdlib.h>
                      #include <errno.h>
                      
                      #define BLOC 8
                      
                      typedef long ssize_t;
                      
                      ssize_t _getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
                      {
                         int c;
                         ssize_t len;
                         char *tmp;
                         
                         len = 0;
                         
                         if(n == NULL || lineptr == NULL || stream == NULL)
                         {
                            errno = EINVAL;
                            return -1;
                         }
                         
                         if(*lineptr == NULL)
                         {
                            *n = 0;
                         }
                         
                         while((c = getc(stream)) != EOF)
                         {
                            if(len >= (ssize_t)*n - 1)
                            {
                               if((tmp = realloc(*lineptr, (*n + BLOC) * sizeof(char))) == NULL)
                               {
                                  return -1;
                               }
                               *lineptr = tmp;
                               (*n) += BLOC;
                            }
                            (*lineptr)[len] = c;
                            ++len;
                            
                            if(c == delim)
                               break;
                         }
                      
                         if(*lineptr != NULL)
                         {
                            (*lineptr)[len] = '\0';
                         }
                         
                         return (len > 0) ? len : -1;
                      }
                      
                      ssize_t _getline(char **lineptr, size_t *n, FILE *stream)
                      {
                         return _getdelim(lineptr, n, '\n', stream);
                      }
                      
                      int main(void)
                      {
                         char *buff;
                         size_t size;
                         ssize_t len;
                         
                         buff = NULL;
                         
                         while((len = _getline(&buff, &size, stdin)) > 0)
                         {
                            printf("##########################################\n");
                            printf("Size = %u, Len = %ld\n", size, len);
                            printf("Buff = %s\n", buff);
                         }
                         
                         if(buff != NULL)
                         {
                            free(buff);
                         }
                         return 0;
                      }
                      
                      • Partager sur Facebook
                      • Partager sur Twitter
                        3 février 2011 à 10:43:21

                        Citation : Tosh

                        Ma solution :


                        A l'inverse du code de Pouet_forever, il me semble que ton code ne retourne jamais -1 en cas d'EOF, même s'il n'y a rien à lire. Ça ne me semble pas correspondre au manuel.
                        Plus grave, si le realloc échoue, il y a une fuite de mémoire.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          3 février 2011 à 10:53:51

                          Effectivement, j'avais oublié que realloc ne libérait pas la mémoire si elle échouait.

                          Et je n'ai pas bien compris quand il fallait retourner -1 pour le EOF...

                          Je pense avoir tout de même corrigé ces deux points.
                          • Partager sur Facebook
                          • Partager sur Twitter
                            3 février 2011 à 12:09:01

                            @Tosh:

                            Citation : gouttegd


                            D’après moi, il faut comprendre que getline returne -1 si la fin de fichier a empêché toute lecture (donc si EOF est le premier caractère à sortir du flux). Si getline a lu ne serait-ce qu’un seul caractère avant de tomber sur l’EOF, elle ne doit pas interpréter ça comme une erreur mais plutôt comme une condition normale de fin de ligne, même si le caractère de fin de ligne lui-même n’a pas été lu.


                            Une autre version.
                            #include <stdio.h>
                            #include <stdlib.h>
                            
                            #define MIN_BUF     32
                            #define EXTEND      1.5
                            
                            static int alloc(char **p, size_t *n)
                            {
                                *n = MIN_BUF;
                                *p = malloc(*n);
                            
                                if(!*p)
                                    return -1;
                                return 0;
                            }
                            
                            
                            
                            static int extend(char **p, size_t *n)
                            {
                                *n *= EXTEND;
                                *p = realloc(*p, *n);
                            
                                if(!*p)
                                    return -1;
                                return 0;
                            }
                            
                            
                            
                            static int readstr(char **lineptr, size_t* n, int delim, FILE* stream)
                            {
                                int c;
                                char *pos = *lineptr;
                            
                                while((c = fgetc(stream)) != EOF)
                                {
                                    pos[0] = c;
                                    pos[1] = '\0';
                            
                                    if(*pos++ == delim)
                                        break;
                                    if(*n - (pos - *lineptr) < 2 && extend(lineptr, n) < 0)
                                        return -1;
                                }
                            
                                return (c == EOF && pos == *lineptr)? -1 : pos - *lineptr;
                            }
                            
                            
                            
                            int my_getdelim(char** lineptr, size_t* n, int delim, FILE* stream)
                            {
                                if(!lineptr || !n || !stream)
                                    return -1;
                            
                                if(*lineptr == NULL && alloc(lineptr, n) < 0)
                                    return -1;
                            
                                return readstr(lineptr, n, delim, stream);
                            }
                            
                            
                            
                            int my_getline(char** lineptr, size_t* n, FILE* stream)
                            {
                                return my_getdelim(lineptr, n, '\n', stream);
                            }
                            
                            
                            
                            int main(void)
                            {
                                char *buf = NULL;
                                size_t n;
                                int ret = my_getline(&buf, &n, stdin);
                            
                                if(ret >= 0)
                                    printf("%d, %s\n", ret, buf);
                            
                                free(buf), buf = NULL;
                            
                                return 0;
                            }
                            
                            • Partager sur Facebook
                            • Partager sur Twitter
                            Zeste de Savoir, le site qui en a dans le citron !
                              3 février 2011 à 13:09:09

                              Citation : GurneyH

                              Une autre version.


                              Bogue de dépassement de tampon si l'on passe un tampon de 0 ou 1 byte.

                              @Tosh: Pour le EOF, la problématique est simple: il faut pouvoir utiliser getline pour lire un fichier texte, ligne par ligne, y compris la dernière ligne même si elle n'est pas terminée par '\n'. Mais une fois la dernière ligne lue, il faut que ça retourne -1.
                              • Partager sur Facebook
                              • Partager sur Twitter
                                3 février 2011 à 13:45:33

                                Citation : Marc Mongenet


                                Bogue de dépassement de tampon si l'on passe un tampon de 0 ou 1 byte.


                                Oui, et il n'y a pas qu'un problème... :honte:


                                edit:
                                Bon, comme j'ai modifié la fonction extend et readstr, je reposte le code entier.
                                Dommage je pensais que c'était une bonne idée de multiplier la taille du buffer par 1.5 :-°
                                #include <stdio.h>
                                #include <stdlib.h>
                                
                                #define MIN_BUF     32
                                
                                static int alloc(char **p, size_t *n)
                                {
                                    *n += MIN_BUF;
                                    *p = malloc(*n);
                                
                                    if(!*p)
                                        return -1;
                                    return 0;
                                }
                                
                                
                                
                                static int extend(char **p, size_t *n)
                                {
                                    *n += MIN_BUF;
                                    *p = realloc(*p, *n);
                                
                                    if(!*p)
                                        return -1;
                                    return 0;
                                }
                                
                                
                                
                                static int readstr(char** lineptr, size_t* n, int delim, FILE* stream)
                                {
                                    int c;
                                    char *pos = *lineptr;
                                
                                    while((c = fgetc(stream)) != EOF)
                                    {
                                        if(*n - (pos - *lineptr) < 2)
                                        {
                                            int diff = pos - *lineptr;
                                            if(extend(lineptr, n) < 0)
                                                return -1;
                                            pos = *lineptr + diff;
                                        }
                                
                                        pos[0] = c;
                                        pos[1] = '\0';
                                
                                        if(*pos++ == delim)
                                            break;
                                
                                    }
                                
                                    return (c == EOF && pos == *lineptr)? -1 : pos - *lineptr;
                                }
                                
                                
                                
                                int my_getdelim(char** lineptr, size_t* n, int delim, FILE* stream)
                                {
                                    if(!lineptr || !n || !stream)
                                        return -1;
                                
                                    if(*lineptr == NULL && alloc(lineptr, n) < 0)
                                        return -1;
                                
                                    return readstr(lineptr, n, delim, stream);
                                }
                                
                                
                                
                                int my_getline(char** lineptr, size_t* n, FILE* stream)
                                {
                                    return my_getdelim(lineptr, n, '\n', stream);
                                }
                                
                                
                                
                                int main(void)
                                {
                                    char *buf = NULL;
                                    size_t n;
                                    int ret = my_getline(&buf, &n, stdin);
                                
                                    if(ret >= 0)
                                        printf("%d, %s\n", ret, buf);
                                
                                    free(buf), buf = NULL;
                                
                                    return 0;
                                }
                                
                                • Partager sur Facebook
                                • Partager sur Twitter
                                Zeste de Savoir, le site qui en a dans le citron !
                                  3 février 2011 à 13:48:04

                                  Citation : Marc Mongenet


                                  @Tosh: Pour le EOF, la problématique est simple: il faut pouvoir utiliser getline pour lire un fichier texte, ligne par ligne, y compris la dernière ligne même si elle n'est pas terminée par '\n'. Mais une fois la dernière ligne lue, il faut que ça retourne -1.



                                  Je me rends compte que ma fonction ne suit pas ce comportement, je corrige donc :


                                  #include <stdio.h>
                                  #include <stdlib.h>
                                  #include <limits.h>
                                  
                                  
                                  
                                  long
                                  my_getline (char **line, size_t *n, FILE *file)
                                  {
                                          size_t size = 0;
                                          int c;
                                  
                                          if (!line || !n || !file) {
                                                  return -1;
                                          }
                                  
                                          if (!line[0]) {
                                                  if (!(line[0] = malloc (sizeof **line * UCHAR_MAX))) {
                                                          return -1;
                                                  }
                                  
                                                  *n = UCHAR_MAX;
                                          }
                                         
                                          while ((c = getc (file)) != EOF) {
                                                  if (*n < size) {
                                                          char *tmp;
                                  
                                                          if ((*n <<= 1) < size) {
                                                                  *n = (size_t) -1;
                                                                  line[0][size] = '\0';
                                                                  return -1;
                                                          }
                                  
                                                          if (!(tmp = realloc (line[0], *n))) {
                                                                  *n >>= 1;
                                                                  line[0][size] = '\0';
                                                                  return -1;
                                                          }
                                  
                                                          line[0] = tmp;
                                                  }
                                  
                                                  line[0][size++] = c; 
                                                  if (c == '\n') {
                                                          break;
                                                  }
                                          }
                                  
                                          line[0][size] = '\0';
                                          return (!size) ? -1 : size;
                                  }
                                  
                                  
                                  int
                                  main (void)
                                  {
                                          char *buffer;
                                          size_t size;
                                          long nb;
                                  
                                          if ((nb = getline (&buffer, &size, stdin)) != -1) {
                                                  printf ("%ld caractères lu:\n", nb);
                                                  printf ("%s", buffer);
                                          }
                                  
                                          free (buffer);
                                          return EXIT_SUCCESS;
                                  }
                                  



                                  EDIT : petite correction
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    3 février 2011 à 14:00:15

                                    @Marc Mongenet : merci pour l'éclaircissement.
                                    A priori, mon implémentation devrait être correct désormais.

                                    @Taurre : Pourquoi mettre size de type size_t alors que ta fonction renvoie un long ? (Elle devrait même renvoyer un ssize_t d'après le manuel)
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      3 février 2011 à 14:18:24

                                      Citation : Tosh


                                      @Taurre : Pourquoi mettre size de type size_t alors que ta fonction renvoie un long ? (Elle devrait même renvoyer un ssize_t d'après le manuel)



                                      En fait, j'utilise le type size_t pour la variable size pour pouvoir la comparer avec la variable n. Je n'utilise pas le type ssize_t parce qu'il est propre à POSIX, comme il s'agit d'un nombre signé, je le remplace par un long(histoire d'être certains d'avoir au moins 4 octets). Maintenant c'est vrai que la valeur de size pourrais dépasser la valeur maximale d'un long, mais je ne vois pas trop quoi faire d'autre puisque la fonction doit retourner -1...
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        3 février 2011 à 14:39:46

                                        Le manuel dit que *lineptr est mis à jour quand c'est nécessaire.
                                        J'aurais tendance à penser que cela signifie que getline ne met jamais à jour *lineptr quand elle retourne -1.

                                        @Taurre: Si on passe un tableau de zéro caractère (note: on peut allouer 0 byte avec malloc), alors ta fonction écrit hors du tableau si elle rencontre immédiatement EOF.
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          3 février 2011 à 14:57:25

                                          Je viens de faire une correction pour la dernière remarque de Marc Mongenet.

                                          @Taurre : peut être caster la comparaison ? D'ailleurs, dans mon code, je pourrais aussi vérifier que len et n ne dépasse pas la capacité de leurs types, mais j'imagine que realloc échouera avant le débordement.
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            3 février 2011 à 15:16:11

                                            Citation : Marc Mongenet


                                            @Taurre: Si on passe un tableau de zéro caractère (note: on peut allouer 0 byte avec malloc), alors ta fonction écrit hors du tableau si elle rencontre immédiatement EOF.



                                            Voilà un cas auquel je n'aurais jamais pensé. En plus, je me rends compte que dans cette hypothèse, s'il y a des caractères à lire, ma fonction va mettre n à (size_t) -1 et va également écrire hors du tableau. De même si l'on passe un tableau de taille zero et que je le passe à la fonction realloc. >_<
                                            Merci beaucoup pour cette remarque, je corrige.

                                            Citation : Tosh


                                            @Taurre : peut être caster la comparaison ? D'ailleurs, dans mon code, je pourrais aussi vérifier que len et n ne dépasse pas la capacité de leurs types, mais j'imagine que realloc échouera avant le débordement.



                                            Si je ne me trompe pas, dans ton code il n'y a pas de risque de débordement de ce côté. Le seul risque c'est que n - 1 dépasse la capacité d'un ssize_t et que la comparaison fasse apparaître len comme supérieur à n alors que ce n'est pas le cas. Cependant, lors de l'appel à realloc, le bloc est bel et bien agrandit puisque n est de type size_t.
                                            Dans mon cas, je pourrais peut-être retourner -1 dans le cas où n dépasse la constante LONG_MAX ?


                                            #include <stdio.h>
                                            #include <stdlib.h>
                                            #include <limits.h>
                                            
                                            
                                            
                                            long
                                            my_getline (char **line, size_t *n, FILE *file)
                                            {
                                                    size_t size = 0;
                                                    int c;
                                            
                                                    if (!line || !n || !file) {
                                                            return -1;
                                                    }
                                            
                                                    if (!line[0]) {
                                                            if (!(line[0] = malloc (sizeof **line * UCHAR_MAX))) {
                                                                    return -1;
                                                            }
                                            
                                                            *n = UCHAR_MAX;
                                                    }
                                                   
                                                    while ((c = getc (file)) != EOF) {
                                                            if (*n < size) {
                                                                    char *tmp;
                                            
                                                                    if ((*n <<= 1) < size || n > LONG_MAX) {
                                                                            return -1;
                                                                    }
                                            
                                                                    if (!(tmp = realloc (line[0], *n))) {
                                                                            *n >>= 1;
                                                                            return -1;
                                                                    }
                                            
                                                                    line[0] = tmp;
                                                            }
                                            
                                                            line[0][size++] = c; 
                                                            if (c == '\n') {
                                                                    break;
                                                            }
                                                    }
                                            
                                                    if (!size) {
                                                            return -1;
                                                    } else {
                                                            line[0][size] = '\0';
                                                            return size;
                                                    }
                                            }
                                            
                                            
                                            int
                                            main (void)
                                            {
                                                    char *buffer;
                                                    size_t size;
                                                    long nb;
                                            
                                                    if ((nb = getline (&buffer, &size, stdin)) != -1) {
                                                            printf ("%ld caractères lu:\n", nb);
                                                            printf ("%s", buffer);
                                                    }
                                            
                                                    free (buffer);
                                                    return EXIT_SUCCESS;
                                            }
                                            

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              4 février 2011 à 1:38:03

                                              J'ai trouvé la documentation POSIX 2008 (IEEE Std 1003.1-2008) de la fonction. Elle est plus précise sur la gestion des erreurs:
                                              http://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetc.html <-- Attention, pas sûr que ce lien soit permanent...

                                              Je dois encore relire mon implémentation pour être sûr qu'elle fonctionne parfaitement...
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                5 février 2011 à 14:08:41

                                                Voici une implémentation:
                                                #include <stdio.h>
                                                #include <stdlib.h>
                                                #include <errno.h>
                                                #include <unistd.h>
                                                
                                                #define GETLINE mygetline
                                                
                                                #define GETLINE_EXPAND 1
                                                
                                                ssize_t mygetline(char **lineptr, size_t *pn, FILE *stream)
                                                {
                                                	int c;
                                                	size_t read;
                                                
                                                	/* GNU getline returns EINVAL if n or lineptr is NULL,
                                                	   or stream is not valid. */
                                                	if (!lineptr || !pn || !stream) {
                                                		errno = EINVAL;
                                                		return -1;
                                                	}
                                                
                                                	/* Return -1 immediately on EOF stream. End-of-stream
                                                	   indicator should be set by fgetc. */
                                                	c = getc(stream);
                                                	if (c == EOF) return -1;
                                                
                                                	/* Force *pn to zero on null *lineptr. */
                                                	if (!*lineptr) *pn = 0;
                                                
                                                	read = 0;
                                                	do {
                                                		/* Line needs expansion if not big enough
                                                		   for next character and '\0'. */
                                                		if (*pn < read + 2) {
                                                			char *const buf = realloc(*lineptr, *pn + GETLINE_EXPAND);
                                                			if (!buf) {
                                                				// ENOMEM set by realloc according to Unix98
                                                				return -1;
                                                			}
                                                			*lineptr = buf;
                                                			*pn += GETLINE_EXPAND;
                                                		}
                                                
                                                		(*lineptr)[read++] = c;
                                                		if (c == '\n') break;
                                                		c = getc(stream);
                                                	} while (c != EOF);
                                                
                                                	/* Mark line end and return. */
                                                	(*lineptr)[read] = 0;
                                                	return read;
                                                }
                                                
                                                int main(void)
                                                {
                                                	char *buf = 0;
                                                	size_t n;
                                                	char *buf0 = malloc(0);
                                                	size_t n0 = 0;
                                                	char *buf1 = malloc(1);
                                                	size_t n1 = 1;
                                                	char *buf8 = malloc(8);
                                                	size_t n8 = 8;
                                                	ssize_t size;
                                                
                                                	puts("buf");
                                                	while ((size = GETLINE(&buf, &n, stdin)) != -1) {
                                                		fwrite(buf, 1, size, stdout);
                                                		printf("size=%zd n=%zu\n", size, n);
                                                	}
                                                	free(buf);
                                                	puts("buf0");
                                                	while ((size = GETLINE(&buf0, &n0, stdin)) != -1) {
                                                		fwrite(buf0, 1, size, stdout);
                                                		printf("size=%zd n=%zu\n", size, n0);
                                                	}
                                                	free(buf0);
                                                	puts("buf1");
                                                	while ((size = GETLINE(&buf1, &n1, stdin)) != -1) {
                                                		fwrite(buf1, 1, size, stdout);
                                                		printf("size=%zd n=%zu\n", size, n1);
                                                	}
                                                	free(buf1);
                                                	puts("buf8");
                                                	while ((size = GETLINE(&buf8, &n8, stdin)) != -1) {
                                                		fwrite(buf8, 1, size, stdout);
                                                		printf("size=%zd n=%zu\n", size, n8);
                                                	}
                                                	free(buf8);
                                                	return 0;
                                                }
                                                
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  5 février 2011 à 14:34:47

                                                  Citation : Marc Mongenet


                                                  Voici une implémentation:



                                                  Intéressant, il y a juste une chose qui me chiffonne, c'est que la macroconstante GETLINE_EXPAND vale 1. Dans le cas où l'on souhaite que la fonction alloue un tampon pour nous, cela fait pas mal d'appel à realloc...
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    5 février 2011 à 21:48:17

                                                    J'aime beaucoup le style de Marc Mongenet.

                                                    Par exemple, une ligne

                                                    if (!*lineptr) *pn = 0;
                                                    

                                                    C'est très souvent déconseillé alors que c'est naturel finalement!

                                                    Je ne suis pas fan de commentaires, mais dans le code de Marc Mongenet, il apportent quelque chose.

                                                    Sinon, même remarque que Taurre:
                                                    cette réallocation de +1 à chaque fois, il me semble, que c'est ce qu'on souhaite éviter, non?

                                                    Mais, on a toujours pas de code de vrai débutants! ;)
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                    Zeste de Savoir, le site qui en a dans le citron !
                                                      5 février 2011 à 22:48:06

                                                      Citation : GurneyH

                                                      Sinon, même remarque que Taurre:
                                                      cette réallocation de +1 à chaque fois, il me semble, que c'est ce qu'on souhaite éviter, non?


                                                      Marc préfère peut-être que son code exprime exactement ce dont il a besoin, et laisser l’allocateur se charger des optimisations éventuelles.

                                                      On ne sait pas ce qui se passe derrière un malloc/realloc (à moins d’avoir la curiosité de regarder le code, quand il est disponible), mais je pense qu’il n’est pas exclu, par exemple, que l’allocateur réserve automatiquement plus que la quantité de mémoire demandée (surtout si elle est faible), afin que les réallocations suivantes ne coûtent rien. On peut même imaginer des schémas plus sophistiqués où l’allocateur surveille le nombre de réallocations et adapte son comportement en conséquence.

                                                      Laisser l’allocateur optimiser les allocations peut alors être plus efficace que d’essayer d‘estimer grossièrement la quantité de mémoire dont on aura besoin (sachant que dans une fonction comme getline, c’est une estimation totalement pifométrique : on n’a aucun moyen de prévoir la quantité de mémoire nécessaire au total — ça dépend entièrement de l’utilisateur et de ce qu’il donne en entrée au programme).
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        6 février 2011 à 1:34:16

                                                        Citation : Taurre

                                                        Intéressant, il y a juste une chose qui me chiffonne, c'est que la macroconstante GETLINE_EXPAND vale 1.


                                                        En fait j'avais mis 1 pour vérifier un cas limite avec les tests. Mais je pense qu'il faudrait plus (disons 1024) en production.
                                                        A moins que realloc fasse ses propres optimisation comme suggeré par gouttegd. Toutefois, ça ferait beaucoup d'appels à une fonction plutôt connue pour être lente.

                                                        Idéalement, je pense qu'il faut doubler l'allocation à chaque fois:
                                                        #include <stdio.h>
                                                        #include <stdlib.h>
                                                        #include <errno.h>
                                                        #include <unistd.h>
                                                        
                                                        #define GETLINE mygetline
                                                        
                                                        ssize_t mygetline(char **lineptr, size_t *pn, FILE *stream)
                                                        {
                                                        	int c;
                                                        	size_t expand;
                                                        	size_t read;
                                                        
                                                        	/* GNU getline returns EINVAL if n or lineptr is NULL,
                                                        	   or stream is not valid. */
                                                        	if (!lineptr || !pn || !stream) {
                                                        		errno = EINVAL;
                                                        		return -1;
                                                        	}
                                                        
                                                        	/* Return -1 immediately on EOF stream. End-of-stream
                                                        	   indicator should be set by fgetc. */
                                                        	c = getc(stream);
                                                        	if (c == EOF) return -1;
                                                        
                                                        	/* Force *pn to zero on null *lineptr. Set expand to
                                                        	   a value bigger than *pn, in case *pn is zero. */
                                                        	if (!*lineptr) {
                                                        		*pn = 0;
                                                        	}
                                                        	expand = *pn + 1;
                                                        
                                                        	read = 0;
                                                        	do {
                                                        		/* Line needs expansion if not big enough
                                                        		   for next character and '\0'. */
                                                        		if (*pn < read + 2) {
                                                        			char *const buf = realloc(*lineptr, *pn + expand);
                                                        			if (!buf) {
                                                        				// ENOMEM set by realloc according to Unix98
                                                        				return -1;
                                                        			}
                                                        			*lineptr = buf;
                                                        			*pn += expand;
                                                        			expand *= 2;
                                                        		}
                                                        
                                                        		(*lineptr)[read++] = c;
                                                        		if (c == '\n') break;
                                                        		c = getc(stream);
                                                        	} while (c != EOF);
                                                        
                                                        	/* Mark line end and return. */
                                                        	(*lineptr)[read] = 0;
                                                        	return read;
                                                        }
                                                        
                                                        int main(void)
                                                        {
                                                        	char *buf = 0;
                                                        	size_t n;
                                                        	char *buf0 = malloc(0);
                                                        	size_t n0 = 0;
                                                        	char *buf1 = malloc(1);
                                                        	size_t n1 = 1;
                                                        	char *buf8 = malloc(8);
                                                        	size_t n8 = 8;
                                                        	ssize_t size;
                                                        
                                                        	puts("buf");
                                                        	while ((size = GETLINE(&buf, &n, stdin)) != -1) {
                                                        		fwrite(buf, 1, size, stdout);
                                                        		printf("size=%zd n=%zu\n", size, n);
                                                        	}
                                                        	free(buf);
                                                        	puts("buf0");
                                                        	while ((size = GETLINE(&buf0, &n0, stdin)) != -1) {
                                                        		fwrite(buf0, 1, size, stdout);
                                                        		printf("size=%zd n=%zu\n", size, n0);
                                                        	}
                                                        	free(buf0);
                                                        	puts("buf1");
                                                        	while ((size = GETLINE(&buf1, &n1, stdin)) != -1) {
                                                        		fwrite(buf1, 1, size, stdout);
                                                        		printf("size=%zd n=%zu\n", size, n1);
                                                        	}
                                                        	free(buf1);
                                                        	puts("buf8");
                                                        	while ((size = GETLINE(&buf8, &n8, stdin)) != -1) {
                                                        		fwrite(buf8, 1, size, stdout);
                                                        		printf("size=%zd n=%zu\n", size, n8);
                                                        	}
                                                        	free(buf8);
                                                        	return 0;
                                                        }
                                                        
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter

                                                        [Exercice] getline

                                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                        • Editeur
                                                        • Markdown