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 :

Get Next Line - Problème de compréhension


Sujet :

C

  1. #1
    Futur Membre du Club
    Femme Profil pro
    Epitech
    Inscrit en
    Décembre 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Epitech

    Informations forums :
    Inscription : Décembre 2017
    Messages : 5
    Points : 8
    Points
    8
    Par défaut Get Next Line - Problème de compréhension
    Bonsoir tout le monde!

    Je suis venue avec une version de Get Next Line que j'aimerais comprendre
    Voici le gnl.c:

    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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    #include "get_next_line.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
     
    int		stock(char **overflow, char **line, int *compteur)
    {
    	int		i;
    	char	*save;
     
    	i = 0;
    	while ((*overflow)[i])
    		if ((*overflow)[i] == '\n')
    		{
    			save = memalloc((strlen(*overflow) - i) * sizeof(char));
    			if (!save)
    				return (-1);
    			save = strcpy(save, &((*overflow)[i + 1]));
    			*line = strncpy(*line, *overflow, i);
    			(*line)[i++] = '\0';
    			free(*overflow);
    			*overflow = save;
    			return (1);
    		}
    		else
    			i++;
    	*compteur += i;
    	*line = strncpy(*line, *overflow, i);
    	*line = (char *)realloc(*line, *compteur, *compteur + BUFF_SIZE + 1);
    	free(*overflow);
    	*overflow = NULL;
    	return (0);
    }
     
    int		line_split(char **overflow, char **line, int *compteur)
    {
    	int	i;
     
    	(*line)[*compteur] = '\0';
    	i = 0;
    	while ((*line)[i])
    	{
    		if ((*line)[i] == '\n')
    		{
    			if ((*line)[i + 1])
    			{
    				*overflow = (char *)memalloc((*compteur - i) * sizeof(char));
    				if (!*overflow)
    					return (-1);
    				*overflow = strncpy(*overflow, (*line + i + 1), *compteur - i);
    				(*overflow)[*compteur - i - 1] = '\0';
    			}
    			(*line)[i] = '\0';
    			return (1);
    		}
    		i++;
    	}
    	return (0);
    }
     
    int		initialisation(const int fd, char ***line, char ***overflow)
    {
    	if (fd < 0 || fd > 5000 || !*line || FDS <= 0 || BUFF_SIZE <= 0)
    		return (-1);
    	if (!*overflow)
    	{
    		if (!(*overflow = (char **)memalloc(FDS * sizeof(char *))))
    			return (-1);
    	}
    	if (!(**line = (char *)memalloc((BUFF_SIZE + 1) * sizeof(char))))
    	{
    		free(*overflow);
    		return (-1);
    	}
    	return (0);
    }
     
    int		get_next_line(const int fd, char **line)
    {
    	static	char	**overflow;
    	int				r;
    	int				compteur;
    	int				resultat;
     
    	compteur = 0;
    	if (initialisation(fd, &line, &overflow))
    		return (-1);
    	if (overflow[fd] && (resultat = stock(&(overflow[fd]), line, &compteur)))
    		return (resultat);
    	while ((r = read(fd, (*line + compteur), BUFF_SIZE)) > 0 && (compteur += r))
    		if ((resultat = line_split(&(overflow[fd]), line, &compteur)))
    			return (resultat);
    		else if (r == BUFF_SIZE)
    		{
    			*line = (char *)realloc(*line, compteur * sizeof(char),
    					(compteur + BUFF_SIZE + 1) * sizeof(char));
    			if (!*line)
    				return (-1);
    		}
    	if (r < 0 || ((*line)[compteur] = '\0'))
    		return (-1);
    	return (**line ? 1 : 0);
    }
    Voici le gnl.h:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #ifndef GET_NEXT_LINE_H
    # define GET_NEXT_LINE_H
    # include <strings.h>
    # define BUFF_SIZE 1
    # define FDS 3000
     
    int				get_next_line(const int fd, char **line);
     
    #endif
    Voici le main.c que j'ai réussi à faire:

    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
     
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include "get_next_line.h"
    #include <string.h>
     
    int		main(int argc, char **argv)
    {
    	int		fd;
    	char	*line;
    	int		i;
    	int		ret;
     
    	i = 0;
    	(void)argc;
    	fd = open((argv[1]), O_RDONLY);
    	while (i < 120)
    	{
    		line = (char *)malloc(sizeof(*line) * 1);
    		ret = get_next_line(fd, &line);
    		printf("--|%s\n", line);
    		i++;
    	}
    }
    Le problème est que je n'arrive pas à comprendre comment fonctionnent les fonctions du fichier gnl.c. Je sais quelle fonction fait quoi grâce aux noms de chaque fonction qui est assez explicite (Je suis assez débrouillarde.), mais je ne comprends pas ce qu'elle fait en détail.
    Puis, je n'ai toujours pas réussi à assimiler la méthode du BUFF_SIZE comment un programme se sert de la mémoire.
    Donc si certaines personnes ont du temps à me consacrer et à me supporter, je suis preneuse!

    Merci!

    - Julia

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Citation Envoyé par PrettyLittleLiars Voir le message
    Voici le main.c que j'ai réussi à faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	line = (char *)malloc(sizeof(*line) * 1);
    Donc tu as fais le main et tu viens d'Epitech.
    Bien ce n'est pas brillant
    1. aucun free
    2. aucun free en sachant 1 malloc par tour de boucle
    3. sizeof c'est avec un type
    4. sizeof avec une variable d'accord (mais je me méfierai du compilateur C que tu utilises), mais c'est un char (sizeof(char) == 1)

  3. #3
    Futur Membre du Club
    Femme Profil pro
    Epitech
    Inscrit en
    Décembre 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Epitech

    Informations forums :
    Inscription : Décembre 2017
    Messages : 5
    Points : 8
    Points
    8
    Par défaut
    Me revoilà avec une version améliorée du main:

    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
    int		main(int argc, char **argv)
    {
    	int		fd;
    	char	*line;
     
    	if (argc == 1)
    		fd = 0;
    	else if (argc == 2)
    		fd = open(argv[1], O_RDONLY);
    	else
    		return (2);
    	while (get_next_line(fd, &line) == 1)
    	{
    		putendl(line);
    		free(line);
    	}
    	if (argc == 2)
    		close(fd);
    }
    Le main n'étant pas le problème principal de ce topic, je l'ai quand même repris de zéro avec un peu plus de logique (pour le perfectionnement personnel) et une meilleure gestion de la mémoire (enfin, je pense).
    Merci en tout cas de le notifier, foetus!

    Citation Envoyé par PrettyLittleLiars Voir le message
    Le problème est que je n'arrive pas à comprendre comment fonctionnent les fonctions du fichier gnl.c.

  4. #4
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Nous aurions besoin d'un peu plus d'info à propos du contexte.

    D'où est-ce que vient ce code ? Il y a plusieurs points suspects : overengineering, utilisation de strncpy en lieu et place de memcpy, cast de la valeur de retour des fonctions d'allocation dynamique, par ailleurs sur-utilisées selon moi, etc..

    Pourrait-on également connaître la spécification de ce get_next_line ? Autrement dit, avant de t'aider à comprendre comment la fonction fait ce qu'elle fait, pourrait-on savoir ce qu'elle est sensée faire ? Quelles en sont les pré- et post-conditions ? J'imagine que l'exercice est accompagné d'un énoncé (quoique, connaissant les fabriques à robots comme Epitech..).

  5. #5
    Futur Membre du Club
    Femme Profil pro
    Epitech
    Inscrit en
    Décembre 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Epitech

    Informations forums :
    Inscription : Décembre 2017
    Messages : 5
    Points : 8
    Points
    8
    Par défaut
    Oui, bien sur Matt_Houston, au temps pour moi!

    Ecrivez une fonction qui retourne une ligne lue depuis un file descriptor.
    Votre fonction aura le prototype suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int get_next_line(const int fd, char **line);
    Le premier paramètre est le file descriptor depuis lequel lire.
    • Le second paramètre est l’adresse d’un pointeur sur caractère qui servira à stocker
    la ligne lue sur le file descriptor.
    • La valeur de retour peut être 1, 0 ou -1 selon qu’une ligne a été lue, que la lecture
    est terminée ou bien qu’une erreur est survenue respectivement.
    • Votre fonction get_next_line doit renvoyer son resultat sans le ’\n’.
    • Un appel en boucle à votre fonction get_next_line permettra donc de lire le
    texte disponible sur un descripteur de fichier une ligne à la fois jusqu’à la fin du
    texte, quelque soit la taille du texte en question ou d’une de ses lignes.
    • Assurez-vous que votre fonction se comporte bien lorsqu’elle lit depuis un fichier,
    depuis l’entrée standard, depuis une redirection, etc.
    • Les variables globales sont interdites.
    • Les variables statiques sont autorisées.

    Si je fais des casts, c'est pour compacter les fonctions qui doivent faire 25 lignes maximum. On a pris pour habitude d'en faire à l'école.
    Je ne connais pas le terme overengineering, mais si vous voyez toutes ces fonctions, c'est parce qu'on a été amenés à inventer notre librairie de fonctions autorisées par l'école.

    Merci de m'aider en tout cas, et désolée de ne pas avoir amené ces consignes dès le début. ^^

    - Julia

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Si je fais des casts, c'est pour compacter les fonctions qui doivent faire 25 lignes maximum
    Ça n'a absolument rien à voir!

    Edit: Au passage, ce n'est pas la première fois que get_next_line() (et les problèmes de son prototype imposé qui la rendent inférieure à getline()) est discuté sur ce forum.
    Voir ici par exemple (notamment les fonctions append_char et get_next_line)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Futur Membre du Club
    Femme Profil pro
    Epitech
    Inscrit en
    Décembre 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 30
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Epitech

    Informations forums :
    Inscription : Décembre 2017
    Messages : 5
    Points : 8
    Points
    8
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Ça n'a absolument rien à voir!
    > Ah pardon, je ne savais pas..

    Citation Envoyé par Médinoc Voir le message
    Edit: Au passage, ce n'est pas la première fois que get_next_line() (et les problèmes de son prototype imposé qui la rendent inférieure à getline()) est discuté sur ce forum.
    Voir ici par exemple (notamment les fonctions append_char et get_next_line)
    > Pourquoi pas, mais encore une fois ce n'est pas le problème de ce topic. Le programme marche, mais je voudrais juste que l'on m'aide à comprendre les fonctions, à utiliser les termes techniques parce que je manque de vocabulaire dans ce domaine. Ce manque de vocabulaire technique me fait perdre des points.. Une approche pédagogique serait la bienvenue!

    - Julia

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    OK, j'ai jeté un coup d'œil. Première chose à dire: C'est moche, car ça suppose qu'une fois un fichier ouvert et accédé avec get_next_line(), on ne tentera jamais de le lire avec une autre fonction (sinon on loupera des trucs).

    Pourquoi dis-je cela? À cause de la variable static char **overflow; dans get_next_line(). Il s'agit d'un tableau de buffers (géré pas très efficacement, d'ailleurs) contenant pour chaque descripteur, une liste de caractères "lus en avance" mais pas encore retournés car ils ne font pas partie de la ligne courante (mais des lignes suivantes). Or, le fait de bufferiser la lecture d'un fichier ne marche que si toutes les fonctions qui lisent le fichier savent utiliser de concert le même buffer. Ce ne peut pas être le cas ici vu que seule get_next_line() connait leur existence!

    En gros, voici à quoi sert chaque fonction:
    • initialisation() alloue le tableau de pointeurs overflow s'il n'est pas déjà alloué, et alloue le buffer de BUFF_SIZE+1 bytes qui va être lu depuis le fichier.
    • stock() sert à déplacer les caractères lus "en réserve" depuis overflow vers line (en gros, ça vide le buffer petit à petit). La fonction parcoure overflow jusqu'au premier \n trouvé, puis copie la partie "gauche" vers line et la partie droite vers le nouveau buffer save, qui remplace l'overflow (qui est alors détruit) et la fonction retourne 1.
      Si aucun \n n'est trouvé dans overflow, alors il est entièrement copié vers line, puis détruit. line est agrandi pour avoir toujours au moins BUFF_SIZE caractères d'espace libre. Par contre, j'ai du mal à voir comment est géré compteur, parce que la fonction utilise une fonction realloc() NON-STANDARD.
    • line_split(), ça sert à faire l'inverse: Au moment de l'appel, line n'est pas vraiment une ligne, c'est un buffer brut pouvant contenir plusieurs lignes (ou même pas une ligne entière). La fonction cherche le premier \n, et s'il est trouvé, tout ce qui se trouve derrière est copié vers un nouveau buffer d'overflow et line est tronqué par un \0 (mais garde sa taille de (compteur + BUFF_SIZE + 1))
      Un truc qu'on remarque, c'est que tant qu'il reste de l'overflow, line_split() ne sera jamais appelé. Ce qui donne à line_split() la garantie de créer un nouveau buffer d'overflow sans avoir à se soucier d'ajouter des caractères dedans.
    • get_next_line() fait sa tambouille avec tout ça: On initialise (ce qui donne un buffer line d'une taille minimale de BUFF_SIZE+1 -- attention ça ne réutilise jamais le line d'un précédent appel, risque de fuite de mémoire si l'appelant ne s'en rend pas compte), on vide l'overflow tant qu'on en a, puis on démarre la boucle compliquée:
      • D'abord on lit au plus BUFF_SIZE dans le fichier.
      • Si on a lu au moins un caractère et qu'il n'y a pas eu d'erreur, on augmente compteur de la taille lue (vu qu'on sait désormais que ce n'est pas -1).
      • Systématiquement, on appelle line_split() pour couper au premier \n rencontré.
      • Si on a trouvé un \n dans le buffer, c'est gagné! La fonction retourne et line contient la chaîne lue. Si line_split() a échoué (dû à un échec de malloc()), on retourne la valeur d'erreur.
      • Si on n'a pas trouvé de \n, deux possibilités:
        • Si on a lu BUFF_SIZE caractères, alors il faut continuer à lire: On agrandit le buffer line (toujours à (compteur + BUFF_SIZE + 1)) et on poursuit la boucle.
        • Si on n'a pas lu BUFF_SIZE caractères, c'est qu'il n'y a plus rien à lire. Si read() a échoué, on retourne une valeur d'erreur. Sinon, on tronque line à compteur (c'est vraiment une utilisation de "programmeur rockstar" de l'opérateur ||, avec en prime une affectation dans un if(), garantie retourner zéro) et on retourne 1 ou 0 selon que la ligne soit complètement vide ou non.

    Tout ça est programmé de manière vraiment bordellique (notamment en mettant systématiquement au milieu d'une boucle qui "cherche" quelque chose le code qui gère ce qui est trouvé) probablement pour faire rentrer le code dans le carcan Épitesque de 25 lignes (donnant un code moins lisible que s'il n'était pas sujet à ces contraintes arbitraires).
    En gros, ça montre tout le code "malin" dont les jeunes programmeurs C sont si fiers, et qu'ils doivent désapprendre ensuite pour apprendre à programmer défensivement à la place (je suis passé par là, j'ai été un "jeune programmeur C" aussi).
    Edit: Un type qui me montre un code pareil lors d'un entretien d'embauche (si je suis un jour en RH), il aura droit au "on vous écrira", parce qu'un code pareil est in-maintenable. Compare avec celui de mon lien et dis-moi si lui, tu as besoin d'aide pour le comprendre.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

Discussions similaires

  1. [Débutant] problème de compréhension
    Par Sylvester dans le forum Général Java
    Réponses: 18
    Dernier message: 21/07/2005, 09h16
  2. Réponses: 5
    Dernier message: 11/04/2005, 10h21
  3. [C#] Problème de compréhension de System.Convert ET Provider
    Par papouAlain dans le forum Windows Forms
    Réponses: 5
    Dernier message: 18/11/2004, 21h52
  4. onclipevent (problème de compréhension)
    Par stephane eyskens dans le forum Flash
    Réponses: 8
    Dernier message: 24/09/2003, 15h09
  5. Problème de compréhension des ensembles
    Par Cornell dans le forum Langage
    Réponses: 6
    Dernier message: 07/02/2003, 22h07

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