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 :

lire un ligne depuis un file descriptor


Sujet :

C

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2017
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2017
    Messages : 2
    Par défaut lire un ligne depuis un file descriptor
    Bonjour a tous, je suis etudiant a 42 Paris et je suis sur le projet get-next_line qui retourne une ligne lu depuis un fd, j'en suis la et tres franchement je ne sais pas trop dans quelle direction aller.. je ne veux pas de reponse sinon j'irais directement recuperer un code sur GITHUB je veux seulement des pistes.
    je sais que je dois traiter les lignes copiees depuis le fd.. comment ? help plz


    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
    int		get_next_line(const int fd, char **line)
    {
    	int				byt_read = 0;
    	static char		*str_fd;
     
    	if (fd < 1 || line == NULL)
    		return (-1);
    	if (!(*line = (char *)malloc(sizeof(char) * BUFF_SIZE)))
    		return (-1);
    	while ((byt_read = read(fd, *line, BUFF_SIZE)) > 0)
    	{
    		ft_strncat(str_fd, *line, BUFF_SIZE);
    		if (ft_memchr(*line, '\n', BUFF_SIZE))
    			break;
    	}
    	return(1);
    }

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 442
    Par défaut
    Bonjour et bienvenue,

    « fd » signifie file descriptor, soit « descripteur de fichier ». Ce n'est qu'un identifiant à l'usage des routines du système et qui désigne le fichier sur lequel tu souhaites travailler. En substance, c'est même ici un simple nombre entier (autrement dit : un simple numéro).

    Donc, tu ne copies pas à proprement parler de données du fd : tu vas demander au système d'aller lire ces données pour toi et d'aller les déposer dans le buffer que tu auras prévu à cet effet. En ce sens, dès que la ligne while ((byt_read = read(fd, *line, BUFF_SIZE)) > 0) est exécutée, tes données sont déjà dans ton buffer. Ce buffer, tu l'as d'ailleurs alloué toi-même et tu as déposée son adresse dans un pointeur fourni par la routine appelante (via un « pointeur de pointeur » nommé line). Ce sera donc à cette routine de libérer ce buffer avec free() et il ne faudra pas oublier de le faire.

    Donc, a priori le ft_strncat(str_fd… est sans objet sauf s'il y a des clauses supplémentaires à ton exercices que l'on ne connaîtrait pas.

    Maintenant, effectivement, la seule chose que l'on demande de faire ici est de récupérer une « ligne », donc terminée soit par un retour à la ligne, soit par la fin du fichier (soit encore, éventuellement, par un '\0' si on considère que cela doit s'appliquer également aux fichiers).

    La principale raison pour laquelle l'appel read() est généralement imbriqué dans un boucle while est qu'on ne peut pas savoir a priori combien de données il reste encore à lire ni quel va être leur contenu (sous-entendu : s'il s'y trouve un retour à la ligne ou pas). Ce que l'on fait donc en général est, comme cela se produit ici, d'utiliser un buffer interne de taille fixe, de demander la lecture d'un bloc de la taille de ce buffer. On boucle alors seulement si le nombre d'octets lus (nombre renvoyé par read()) est égal à la taille du buffer. Si elle est inférieure mais positive quand même, ou qu'elle est nulle, c'est que la fin du fichier a été atteinte.

    C'est vrai avec un fichier mais ça l'est encore plus quand on lit l'entrée standard, un pipe ou un socket. Dans ces conditions, on ne peut pas savoir à l'avance quelle sera la quantité totale de données transmise ni quand l'émetteur décidera de refermer la connexion.

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    42 ayant été fondé par des gens d'Epitech, je suppose qu'ils suivent les mêmes "normes de codage".

    Ton problème majeur ici, c'est que tu ne peux pas avoir un buffer de lecture (pour éviter de lire un caractère à la fois) dédié à get_next_line(), sinon tu vas perdre des caractères. Si tu veux bosser avec un buffer, tu dois le trimballer avec le descripteur, comme le fait la bibliothèque standard du C dans une structure FILE.

    Code C : 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
    #define BUFFER_SIZE 512
     
    typedef struct myFile
    {
    	int fd;
    	char bufferLecture[BUFFER_SIZE];
    	size_t tailleLecture; /*Taille du contenu actuel du buffer de lecture*/
    	size_t positionLecture; /*Position du prochain caractère à être lu depuis le buffer*/
     
    	char bufferEcriture[BUFFER_SIZE];
    	size_t tailleEcriture; /*Taille du contenu actuel du buffer d'ecriture*/
    	size_t positionEcriture; /*Position du prochain caractère à être lu depuis le buffer*/
    } MY_FILE;
     
    MY_FILE* my_fdopen(int fd)
    {
    	/*Pas besoin de caster si tu utilises un compilateur C*/
    	MY_FILE* pf = malloc(sizeof *pf);
    	if(pf != NULL)
    	{
    		MY_FILE empty = {0};
    		*pf = empty;
    		pf->fd = fd;
    	}
    	return pf;
    }
     
    int my_getc(MY_FILE* pf)
    {
    	/*Faut-il lire depuis le fichier pour remplir le buffer?*/
    	if(pf->positionLecture >= pf->tailleLecture)
    	{
    		int nbRead;
    		pf->tailleLecture = 0;
    		pf->positionLecture = 0;
    		nbRead = read(pf->fd, pf->bufferLecture, BUFFER_SIZE);
    		if(nbRead <= 0)
    			return EOF;
    		pf->tailleLecture = nbRead;
    	}
     
    	/*Lit un caractère dans le buffer*/
    	{
    		char ret = pf->bufferLecture[pf->positionLecture];
    		pf->positionLecture++;
    		return ret;
    	}
    }
    Ensuite, tu as besoin de fonctions pour ajouter des caractères, un par un, à un buffer, en l'agrandissant quand c'est nécessaire:
    Code C : 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
    #define LINE_BUFFER_SIZE 80
     
    int realloc_char(char **ppMem, size_t nNew)
    {
    	int ret = -1;
    	char * pNew = realloc(*ppMem, nNew * sizeof(**ppMem));
    	if(pNew != NULL || nNew==0)
    	{
    		*ppMem = pNew;
    		ret = 0;
    	}
    	return ret;
    }
     
    int append_char(char **ppBuffer, size_t *pTaille, size_t *pPosition, char ch)
    {
    	if(*ppBuffer == NULL || *pPosition >= *pTaille) /*Faut-il agrandir?*/
    	{
    		/*Calcul nouv. taille buffer: Anc. taille *1.5, + cas particuliers*/
    		size_t newTaille;
    		if(*pTaille == 0)
    			newTaille = LINE_BUFFER_SIZE;
    		else
    		{
    			size_t increase = (*pTaille) / 2;
    			if(increase == 0) /*Ceci n'arrive que pour une taille de 1,*/
    				increase++;   /*Mais il faut pouvoir le supporter.*/
    			newTaille = *pTaille + increase;
    			if(*pPosition >= newTaille)
    				newTaille = *pPosition + 1;
    		}
    		if(!realloc_char(ppBuffer, newTaille)) /*Et maintenant on agrandit*/
    			return -1;
    		*pTaille = newTaille;
    	}
    	(*ppBuffer)[ *pPosition ] = ch;
    	(*pPosition)++;
     
            return 0;
    }
    (pas facile de faire rentrer ces fonctions dans 25 lignes de 80 caractères)

    Pour finir, il te suffit d'utiliser ces deux jeux de fonctions ensemble, en boucle:
    Code C : 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
    int free_and_return(void *pMem, int returnValue)
    {
    	free(pMem);
    	return returnValue;
    }
     
    int get_next_line(MY_FILE *pf, char **ppLine)
    {
    	size_t tailleBuffer = LINE_BUFFER_SIZE;
    	char *buffer = malloc(tailleBuffer * sizeof *buffer);
    	*ppLine = NULL;
    	if(buffer != NULL)
    	{
    		size_t positionBuffer = 0;
    		int c;
    		while((c=my_getc(pf)) != EOF && c!='\n')
    		{
    			if(!append_char(&buffer, &tailleBuffer, &positionBuffer, (char)c))
    				return free_and_return(buffer, -1);
    		}
    		/*Ajoute caractere nul*/
    		if(!append_char(&buffer, &tailleBuffer, &positionBuffer, '\0'))
    			return free_and_return(buffer, -1);
    		*ppLine = buffer;
    		return (int)positionBuffer - 1; /*Carac nul non compte, comme strlen*/
    	}
    	return -1;
    }

    Note: Je n'ai pas testé, je n'ai même pas essayé de compiler. Mais c'est l'idée générale.
    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.

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 801
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 801
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par 42noobi Voir le message
    Bonjour a tous, je suis etudiant a 42 Paris et je suis sur le projet get-next_line qui retourne une ligne lu depuis un fd, j'en suis la et tres franchement je ne sais pas trop dans quelle direction aller.. je ne veux pas de reponse sinon j'irais directement recuperer un code sur GITHUB je veux seulement des pistes.
    je sais que je dois traiter les lignes copiees depuis le fd.. comment ? help plz
    Bonjour

    Je vois dans ta fonction un très gros défaut difficile à surmonter: tu risques de lire plus que nécessaire et donc perdre des octets.
    En effet, tu lis par blocs de BUFF_SIZE. Mais tes lignes ne sont pas forcément un multiple de cette valeur.

    Imaginons par exemple un fichier contenant ces 2 lignes
    j'aime le jambon, le saucisson
    j'aime le jambon quand il est bon
    En fait, ces lignes ne sont pas ainsi dans le fichier. Ca, c'est ton éditeur qui les affiche bien proprement. Dans la réalité, ces lignes sont écrites d'un bloc avec un '\n' pour les séparer de cette façon j'aime le jambon, le saucisson\nj'aime le jambon quand il est bon\n.
    Si tu lis ce fichier par bloc de 10 caractères, voici ce que tu vas lire (je mets un "." pour symboliser les espaces)
    j'aime.le.
    jambon,.le
    .saucisson
    \nj'aime.le
    A partir de là, soit ton algo récupère le tout mais ne stocke seulement jusqu'au '\n' (et quid de la suite ?), soit il récupère tout et stocke tout mais tu obtiens ensuite une ligne plus une partie de la suivante. D'après ce que j'ai vu, c'est la seconde option qui est codée mais ce que tu récupères ce n'est pas "une" ligne.

    Une idée simpliste pour régler ce souci serait de ne lire qu'un octet à la fois et le stocker dans ton buffer ligne en l'agrandissant quand c'est nécessaire. Et si tu lis un '\n' alors tu quittes la fonction (sans oublier de terminer ta ligne par un '\0' sinon ce n'est pas une chaine). Ca fonctionnera mais le rendement sera assez médiocre.
    Une autre idée plus poussée serait que tu aies un second buffer de travail (on va l'appeler "X") destiné, lui, à servir de support à la lecture. Ainsi quand tu lis ton fichier par bloc de BUF_SIZE, en réalité tu t'alimentes dans ce buffer X. Et si ce buffer X est vide, alors tu le remplis au préalable par une vraie lecture. Donc quand tu arrives au '\n' tu quittes avec ta ligne remplie mais tout ce qui dépasse reste dans le buffer X. Et à l'appel suivant, tu reprends à cet endroit (sous réserve bien entendu que ton buffer X soit conservé d'appels en appels mais ça c'est ton job)...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2017
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2017
    Messages : 2
    Par défaut
    merci de vos réponses, comme je l'ai dit je suis juste novice donc le code que tu as fait Médinoc, c'est -je pense- exactement ca, je ne vais pas le copier coller bien sur car ce n'est pas le résultat qui m'intéressait, mais plutôt la démarche.
    premierement les structures, c'est le truc avec le lequel je suis le moins a l'aise .. peux tu me l'expliquer stp
    du coup la fonction qui l'utilise egalement

    et oui Sve@r, comment recuperer qu'un octet a la fois et reprendre la ou je me suis arreté, j'ai vu la possibilite de variables statiques, qui je pense peut convenir dans cette situation ..?

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    La structure, c'est juste un bloc réunissant des variables entre elles, par nom.

    Exemple:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct coordonnees
    {
    	int x;
    	int y;
    	int z;
    };
    C'est particulièrement utile si tu as à gérer un tableau: Dans le cas des coordonnees, sans les structures, il te faudrait trois tableaux séparés! Tandis que là, tu peux juste utiliser un tableau de structures:
    Code C : 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
    void affichCoord(struct coordonnees const *co)
    {
    	/*Note:
    		co->x est une façon compacte d'écrire (*co).x
    	*/
    	printf("(%d, %d, %d)\n", co->x, co->y, co->z);
    }
     
    int main(void)
    {
    	struct coordonnees monTableau[10];
    	size_t i;
    	/*On initialise avec des valeurs pseudo-aléatoires*/
    	for(i=0 ; i<10 ; i++)
    	{
    		monTableau[i].x = rand();
    		monTableau[i].y = rand();
    		monTableau[i].z = rand();
    	}
     
    	/*On affiche le tableau*/
    	for(i=0 ; i<10 ; i++)
    		affichCoord(&monTableau[i]);
    }


    Pour les variables statiques, ça ne peut pas convenir, parce qu'il n'y a qu'une seule de chaque variable statique, alors qu'il peut y avoir plusieurs descripteurs de fichiers. Ce qui ne te laisse que deux solutions:
    • soit tu n'utilises pas de buffer de lecture, et tu fais tout simplement des read(fd, &caractere, 1);. C'est lent, mais ça marche: N'oublions pas, l'optimisation prématurée est la racine de tous les maux. ©Donald Knuth
    • soit tu utilises un buffer de lecture pour chaque descripteur, que tu passes systématiquement à toutes les fonctions qui font des lecture sur le descripteur; c'est ce que fait la bibliothèque standard du C avec ses structures FILE. Et c'est ce que fait mon exemple.
      Évidemment, le buffer n'est pas seulement le tableau de char: C'est aussi les entiers qui l'accompagnent, indiquant où est le dernier caractère lu dans le buffer, et combien il en contient (ce qui permet de savoir quand il faut faire un nouveau read()).
    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
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 801
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 801
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par 42noobi Voir le message
    et oui Sve@r, comment recuperer qu'un octet a la fois et reprendre la ou je me suis arreté
    Attention, la lecture d'un octet à la fois ne demande pas que tu reprennes là où tu t'étais arrêté. Cette démarche ne s'envisage que si tu utilises un buffer intermédiaire. C'était deux options à choisir de façon exclusive.
    Mais pour une lecture d'un octet, il te suffit d'écrire char c puis read(fd, &c, 1).

    Citation Envoyé par 42noobi Voir le message
    j'ai vu la possibilite de variables statiques, qui je pense peut convenir dans cette situation ..?
    Les variables statiques sont de fausses bonnes idées. Ca peut convenir effectivement dans un premier temps mais comme il n'y a qu'une seule case mémoire par variable statique, tu ne peux plus utiliser ta fonction dans deux contextes séparés (style lire un fichierA et un fichierB en parallèle) parce que la lecture du fichierB va écraser la donnée stockée lors de la lecture du fichierA. On nomme ce genre de fonctions des fonctions "non réentrantes". Le but d'une variable statique n'est pas de mémoriser un travail en cours pour pouvoir repartir de là et les rares fonctions que je connais qui utilisent ce principe (en fait je ne connais que strtok()) sont généralement déconseillées.
    Donc seule solution pérenne: utiliser une structure de travail comme l'explique Medinoc, structure qui contiendrait ton fd et ledit buffer. Lors de l'appel, tu passes la structure à ta fonction qui peut donc utiliser tous ces élémentss qu'elle contient. Et si t'as deux fichiers à lire, t'auras alors deux variables distinctes qui, étant du type de cette structure, auront chacune leurs éléments distincts.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. [Débutant] Lire une ligne spécifique depuis un fichier
    Par Sperafico dans le forum VB.NET
    Réponses: 7
    Dernier message: 27/01/2012, 13h00
  2. lire une ligne d'un file descriptor
    Par Pitou5464 dans le forum Réseau
    Réponses: 4
    Dernier message: 30/10/2006, 16h13
  3. Réponses: 2
    Dernier message: 10/05/2005, 18h15
  4. [BATCH] lire chaque ligne d'un fichier texte
    Par bartmarley dans le forum Windows
    Réponses: 13
    Dernier message: 22/04/2005, 21h01
  5. [TP]lire une ligne de l'ecran et la stocker dans une chaine
    Par Bleuarff dans le forum Turbo Pascal
    Réponses: 26
    Dernier message: 02/07/2002, 10h08

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