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 :

Récupérer une ligne aléatoire avec fgets


Sujet :

C

  1. #1
    Membre à l'essai
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Mai 2006
    Messages : 12
    Points : 10
    Points
    10
    Par défaut Récupérer une ligne aléatoire avec fgets
    Bonjour,

    Je souhaite récupérer une ligne dans un fichier avec fgets, mais une ligne aléatoire. Par exemple mon fichier sujets.txt contient cela :

    Citation Envoyé par sujets.txt
    Un chien
    Cet arbre
    Une fleur
    Je désire obtenir aléatoirement dans une chaine de caractères "Un chien", "Cet arbre" ou bien "Une fleur". Je me doute qu'il faut déplacer le pointeur mais je ne sais pas comment écrire "Déplacer le pointeur au 4 ième \n" par exemple.

    Merci

  2. #2
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    Il faut d'abord bufferiser le fichier dans un tableau de pointeur vers des chaines de caractères du type "char* t[]" de taille le nombre de ligne du fichier; puis choisir une ligne du tableau au hasard.

  3. #3
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par Alexbad
    Je souhaite récupérer une ligne dans un fichier avec fgets, mais une ligne aléatoire.
    Tu peux compter les lignes (fgetc()), tirer une ligne au hasard, revenir au début (rewind()) et lire la ligne concernée (en comptant les fgets()).

  4. #4
    Membre à l'essai
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Mai 2006
    Messages : 12
    Points : 10
    Points
    10
    Par défaut
    Edit : Emmanuel Delahaye, je regarde ça ^^
    Oui ça semble une bonne solution mais j'ai des problèmes. D'abord, je crois que mon nombre aléatoire n'est pas généré correctement (j'en génère un de 0 à 3) et ça fait 3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 etc. Je tiens à préciser qu'il s'agit d'un script qui tourne en boucle, mais que je récupère le contenu du fichiers en-dehors de la boucle.

    Pour le nombre aléatoire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    srand(time(NULL));
    random = (rand() % (3 - 1 + 1)) + 1;
    Ensuite, pour la lecture du fichier comme tu me l'as suggéré, j'ai tenté :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    FILE *fichier = NULL;
    fichier = fopen("sujets.txt", "a+");
    char *chaine[100] = {NULL};
    for (i = 0; i <= 200; i++)
    {
           fgets(chaine[i], 100, fichier);
    }
    Et là normalement chaque case du tableau devrait contenir une ligne non? Parce que ce n'est pas du tout le cas, le tableau apparaît vide.

  5. #5
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par Alexbad
    Oui ça semble une bonne solution mais j'ai des problèmes. D'abord, je crois que mon nombre aléatoire n'est pas généré correctement (j'en génère un de 0 à 3) et ça fait 3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 etc. Je tiens à préciser qu'il s'agit d'un script qui tourne en boucle, mais que je récupère le contenu du fichiers en-dehors de la boucle.

    Pour le nombre aléatoire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    srand(time(NULL));
    random = (rand() % (3 - 1 + 1)) + 1;
    srand() doit être appelé une seule fois au début du programme (donc hors de toute boucle).
    Ensuite, pour la lecture du fichier comme tu me l'as suggéré, j'ai tenté :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    FILE *fichier = NULL;
    fichier = fopen("sujets.txt", "a+");
    Une lecture, c'est "r", c'est pas "a+".
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    char *chaine[100] = {NULL};
    for (i = 0; i <= 200; i++)
    {
           fgets(chaine[i], 100, fichier);
    }
    Je ne t'ai jamais parlé de tableau de pointeurs. C'est inutile et il manque de la mémoire (un pointeur non initialisé pointe n'importe où, le comportement est indéterminé).

    Par contre, il faut passer à fgets() l'adresse du premier élément d'un tableau de char de taille suffisante.

    Enfin, il faut tester le retour de fgets() pour savoir si on une fin de lecture s'est produite.

    Si tu ne connais pas une fonction, il faut lire son mode d'emploi avant de l'utiliser, sinon, tu cours à la catastrophe...

    http://man.developpez.com/

  6. #6
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int nombre_de_ligne=0;
    char buffer[256]={};
    while(fgets(buffer,sizeof(buffer),fichier)){nombre_de_ligne++;}
    devrait aller pour déterminer le nombre de ligne dans le fichier.

  7. #7
    Membre à l'essai
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Mai 2006
    Messages : 12
    Points : 10
    Points
    10
    Par défaut
    Ok et après, pour lire la ligne concernée je fais quelque chose du genre? (me corriger si je fais des erreurs de syntaxe) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    rewind(fichier);
    int i = 0;
    for (i = 0; i < nombre_de_lignes; i++)
    {
         fgets(chaine, 100, fichier);
         if (i == random)
         {
              //J'affiche la ligne
         }
    }
    EDIT: je sais pas pourquoi, mais il refuse de piger dans plus que les 5 premières lignes :s .

  8. #8
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    Est-ce que plusieures lectures aléatoires doivent être faites?
    Si oui bufferiser le fichier permettrait d'optimiser en cherchant la ligne dans le buffer à partir de son indice.

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

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Points : 6 498
    Points
    6 498
    Par défaut
    Perso, je n'aime pas beaucoup cette relecture systématique du fichier.
    Comme le disait seriousme, il vaut mieux mémoriser les lignes du fichier dans un tableau puis afficher la nième ligne à la demande, (si cette action est répétitive).
    Chacun ses goûts.

  10. #10
    Membre à l'essai
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : Mai 2006
    Messages : 12
    Points : 10
    Points
    10
    Par défaut
    Citation Envoyé par Trap D
    Perso, je n'aime pas beaucoup cette relecture systématique du fichier.
    Comme le disait seriousme, il vaut mieux mémoriser les lignes du fichier dans un tableau puis afficher la nième ligne à la demande, (si cette action est répétitive).
    Chacun ses goûts.

    Dans mon cas c'est pas une question de goût, c'est seulement que je ne suis pas encore à l'aise avec les pointeurs (et seriousme m'en a suggéré au début), alors que ma solution de parcourir le fichier avec un for fonctionne bien.

    Là j'ai un autre problème, c'est qu'à chaque fois que j'affiche une ligne prise aléatoirement, il y a un symbole [] (un carré), je crois que c'est parce que la fin de la chaîne n'est pas détecté OU que la case qui la termine est vide, possible? Comment régler ça?

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

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Points : 6 498
    Points
    6 498
    Par défaut
    Celà vient peut-être de la lecture par fgets qui laisse le '\n' dans la chaîne lue (c'est d'ailleurs comme celà qu'on sait si on a lu toute la ligne où pas).
    La manière classique de s'en débarasser est :
    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
    rewind(fichier);
    int i = 0;
    for (i = 0; i < nombre_de_lignes; i++)
    {
         fgets(chaine, 100, fichier);
         if (i == random)
         {
            char *p;
            if (( p = strchr(chaine, '\n')) != NULL)
              *p = 0;
            else
            // ici le buffer de lecture est trop petit
            {
              // a toi de voir ce que tu veux faire 
            }
              //J'affiche la ligne
         }
    }

  12. #12
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Perso, je n'aime ni l'une ni l'autre des solutions proposées.
    Je n'aime pas l'idé de stocker les lignes du fichier en mémoire car avec un fichier de 50Mo je vais devoir allouer 50Mo (et des poussières) pour finalement n'afficher qu'une seule ligne. Le coût me paraît trop important comparé au résultat.
    Je n'aime pas l'idée de lire les lignes du fichier avec fgets() pour les compter puis relire les N (avec N le nombre tiré au sort) premières lignes pour n'afficher que la Nième...d'autant que si certaines lignes sont très longues et qu'on considère qu'on ne connaît pas leur longueur maximum on va devoir aussi gérer ça...

    Je propose donc une solution alternative :
    • Créer un tableau dynamique d'entiers longs ("long") qui servira à stocker l'offset du début de chaque ligne ;
    • Ouvrir le fichier en mode binaire ;
    • Lire le fichier octet par octet en détectant les fins de ligne et en ajoutant l'offset de chaque début de ligne au tableau ;
    • Tirer un numéro de ligne au sort ;
    • Lire l'offset de début de cette ligne dans le tableau ;
    • Faire un "fseek(fichier, offset, SEEK_SET)" pour se placer au début de la ligne voulue ;
    • Lire (et afficher) tous les caractères à partir de là jusqu'à la fin de la ligne ;
    • Fermer le fichier.


    Résultat :
    • besoin en mémoire moindre (et de loin !)
    • pas de deuxième lecture des N premières lignes (en considérant que le fichier n'est pas sur une bande magnétique et que fseek() n'est pas implémenté avec des read() )
    • pas besoin de gérer la taille du buffer avec fgets()
    • exécution plus rapide
    • cela reste portable


    Voilà-voilà...

  13. #13
    Membre expert
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 3 958
    Points
    3 958
    Par défaut
    exécution plus rapide
    Pas pour des lectures nombreuses, accéder à la ressource est plus long que trouver la ligne dans un buffer avec son indice.

  14. #14
    Membre confirmé Avatar de Lunixinclar
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2006
    Messages
    416
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 416
    Points : 489
    Points
    489
    Par défaut
    Autre méthode pour ne lire qu'une seule fois le fichier:
    Une ligne peut être identifiée par une position de début, et une position de fin.

    Le première étape consisterait à connaitre le nombre de lignes: ouvrir le fichier
    avec fopen(".txt","r") et lire fgetc(pF), si c'est un '\n' (attention *nix only)
    stocker sa position (incrémentation d'un compteur) dans un tableau d'entiers t[]
    (int=attention aux gros fichiers).

    Le tableau contiendrait grosso-modo les valeurs 12,19,35.

    A ce stade, choisir le nombre aléatoire n. Pour extraire la ligne, placer le curseur
    avec fsetpos() sur la position t[n-1] et lire jusqu'à la position t[n].
    Exception: si c'est la 1re ligne, placer le curseur au début.

    *edit*: on a posté en même temps, ....

  15. #15
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par David.Schris
    Je n'aime pas l'idée de lire les lignes du fichier avec fgets()
    Bzzt. Je n'ai pas dit fgets(), mais fgetc().

  16. #16
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Citation Envoyé par seriousme
    Pas pour des lectures nombreuses, accéder à la ressource est plus long que trouver la ligne dans un buffer avec son indice.
    J'admets avoir ajouté "exécution plus rapide" par provocation...partiellement... Je pensais aussi au cas d'un gros fichier (genre une collection d'encyclopédies stockée dans un fichier de 600Mo [1]) qui ne tiendrait pas facilement en mémoire et obligerait le système à utiliser le "swap" : ton programme serait ralenti et tous les autres avec (justement à cause du temps nécessaire pour accéder à la ressource, comme tu le dis si bien )...
    Et puis l'idée de stocker le fichier complet pour n'en afficher qu'une ligne ne me plaît pas de toutes façons.


    Citation Envoyé par Lunixinclar
    *edit*: on a posté en même temps, ....
    On dit ça, on dit ça... J'aime bien ton idée...


    Citation Envoyé par Emmanuel Delahaye
    Bzzt. Je n'ai pas dit fgets(), mais fgetc().
    Menteur
    Citation Envoyé par Emmanuel Delahaye
    Par contre, il faut passer à fgets() l'adresse du premier élément d'un tableau de char de taille suffisante.
    Citation Envoyé par Emmanuel Delahaye
    et lire la ligne concernée (en comptant les fgets()).

    [1] - l'exemple paraît "tiré par les cheveux" ? On doit pouvoir trouver des exemples plus concrets (avec le même programme lancé plusieurs fois en même temps sur des fichiers plus petits, par exemple).

  17. #17
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985

  18. #18
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Citation Envoyé par [url=http://www.developpez.net/forums/showpost.php?p=1216596&postcount=3]http://www.developpez.net/forums/showpost.php?p=1216596&postcount=3[/url]
    Tu peux compter les lignes (fgetc()), tirer une ligne au hasard, revenir au début (rewind()) et lire la ligne concernée (en comptant les fgets()).
    "Dialogue de sourds" ?
    Il est possible que j'ai mal compris.
    Dans ce cas, une explication sur ce que tu as voulu dire par "en comptant les fgets()" serait bienvenue. Plus en tous cas qu'un simple lien qui renvoie, justement, vers "en comptant les fgets()".

    PS: oui, j'ai vu qu'à la première lecure tu comptais les lignes avec fgetc(). Mais pas après l'appel "rewind()".

  19. #19
    Membre confirmé Avatar de Lunixinclar
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2006
    Messages
    416
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 416
    Points : 489
    Points
    489
    Par défaut
    Aie. je viens de regarder la source de GREP. Ligne 251 dans Reset_the_buffer_for_a_new_file() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    buffer = xmalloc (bufalloc);
    Bon chacun fait comme il veut hein, tant que le free n'est pas oublié.

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

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Points : 6 498
    Points
    6 498
    Par défaut
    La morale de l'histoire, si on ne tient pas compte de discussions + ou - teintées de mauvaise foi (je n'ai pas tout suivi), est que la solution optimum dépend de la taille du fichier considéré et qu'il faudrait faire des tests pour arriver à une conclusion satisfaisante.
    On peut peut-être considéré le sujet comme clos ?

Discussions similaires

  1. [MySQL] Récupérer une ligne aléatoire dans MySQL
    Par heretik25 dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 03/01/2012, 17h39
  2. Récupérer une ligne aléatoirement
    Par Cyanatide dans le forum Requêtes
    Réponses: 6
    Dernier message: 29/11/2011, 11h28
  3. [HTML] comment récupérer une ligne d'un tableau avec HTML?
    Par jaafarerraji dans le forum Balisage (X)HTML et validation W3C
    Réponses: 5
    Dernier message: 01/10/2007, 01h21
  4. [VBA-E]selectionner une ligne repérée avec activecell
    Par titou007 dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 31/03/2006, 16h24
  5. comment obtenir une ligne aléatoirement
    Par titoumimi dans le forum Langage SQL
    Réponses: 2
    Dernier message: 18/05/2005, 16h52

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