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

  1. #1
    Nouveau Candidat au Club
    Rechercher un caractère dans un fichier et afficher tous les résultats
    Bonjour tout le monde,

    Je viens de débuter en C, je fais beaucoup d'exercices pour m'entraîner mais malheureusement je suis tombé sur un os, je vous explique. Pour ceux qui connaissent "Unix" je dois faire un code qui fait la même chose que "grep", mais une des conditions c'est de ne pas utiliser la librairie "string.h" et aussi d'utiliser deux champs de caractère "argv[1]" et "argv[2]" qui doivent être dans le main, le argv[1] doit prendre le champs de caractère à rechercher et argv[2] doit prendre le nom du fichier, je sais comment faire pour ouvrir un fichier mais malheureusement la où je coince c'est la recherche du caractère et aussi de sortir tous les éléments du fichier. Je pense que je dois utiliser un "Regex".. J'ai vraiment besoin de votre aide je vous en serai très reconnaissant

  2. #2
    Expert éminent
    Je pense que si tu ne peux pas utiliser la bibliothèque string.h c'est pour ne pas utiliser les fonctions strcmp, strncmp, et petites sœurs (<- 2 liens cplusplus.com en anglais)

    Donc regarde ce fil de discussion et mes 2 réponses : celle algorithmique et celle avec du code
    En gros, il faut coder 2 boucles : 1 pour parcourir ton texte et 1 autre pour tester caractères par caractères le mot à rechercher et ton texte (et comme je le disais, en cas d'échec de reconnaissance, il faut revenir au premier caractère testé "positif" à cause des motifs qui se répètent)

    Et après, il faut te poser 1 question : est-ce que ton mot à rechercher peut contenir les caractères de fin ligne '\n' (Line Feed) et/ ou '\r' (Carriage Return) ou pas ?
    • Si non, tu peux lire ton fichier ligne par ligne avec la fonction fgets (<- lien cplusplus.com en anglais)
    • Si oui (mais également non ), il faut charger ton fichier dans 1 buffer (en gros, c'est 1 grosse chaîne de caractères). Il me semble qu'il faut lire 1 fois ton fichier pour calculer le nombre de caractères (avec la fonction fgetc) allouer 1 buffer avec ce nombre de caractères, le remplir en relisant ton fichier, faire ta recherche et libérer la mémoire. Ouais, c'est nettement plus long

  3. #3
    Nouveau Candidat au Club
    Je te remercie pour ta réponse super clair ! Je comprends la majorité de ce que tu me dis mais il y a juste parcourir la chaîne de caractère je ne vois pas très bien comment faire, peux tu m'expliquer plus en détail s'il te plaît ? Ou me donner un exemple ?

  4. #4
    Expert éminent
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
     
     
    /*****************************************************************************/
    /***********************************  Main  **********************************/
    /*****************************************************************************/
     
    int main(int argc, char** argv)
    {
        char str[] = "Qu'en pensez-vous ?"; // constant string
     
        char* c;
        size_t len = strlen(str); // <- use string.h
        size_t index;
     
    //  Method 1 : index    
        for(index=0; index < len; ++index) {
            printf("%02lu: %c\n", (index + 1), str[index]);
        }
     
    //  Method 2 : pointer
        for(c=str, index=1; (*c) != '\0'; ++c, ++index) {
            printf("%02lu: %c\n", index, (*c));
        }
     
     
        return EXIT_SUCCESS;
    }

  5. #5
    Nouveau Candidat au Club
    Je te remercie malheureusement je n'y arrive pas, je laisse tomber ahah c'est trop compliqué je n'arrive qu'à ouvrir un fichier, merci quand même pour ton aide

  6. #6
    Expert confirmé
    Citation Envoyé par Ivankof51 Voir le message
    Je te remercie malheureusement je n'y arrive pas, je laisse tomber ahah c'est trop compliqué je n'arrive qu'à ouvrir un fichier, merci quand même pour ton aide
    Bonjour.

    Il ne faut pas baisser les bras à la première difficulté rencontrée.

    Il faut intégrer certains concepts. Je pars comme situation de base que les chaînes sont codées en ASCII. C'est à dire que toutes les lettres rencontrées seront comprises entre 1 et 255. Pour exemple la lettre A est codée en 65 décimal.

    Une chaîne de caractères n'est qu'un simple tableau. La norme en C veut que cette chaîne se termine par un code spécial qui indique que la chaîne est finie. Ce code est '\0'.

    Par exemple la chaîne suivante "Bonjour" sera codée ainsi :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    66 111 110 106 111 117 114 0

    Donc ce tableau contiendra le nombre de lettres + 1 pour le caractère de fin.

    foetus utilise strlen(str); qui fait partie de string.h. Tu peux déjà reconstruire cette fonction si tu en besoin en interne. Il te suffit de parcourir le tableau en incrémentant un compteur jusqu'à rencontrer le caractère '\0'.

    Tu peux aussi travailler la chaîne dans une simple boucle while ();. Dans l'exemple suivant je n'utilise pas les pointeurs pour ne pas te perdre. Je conserve l’écriture sous forme de tableau.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    char text []= "Bonjour";
    int i = 0; // index pour se déplacer dans la chaîne. 0 correspond à la première lettre de la châine.
     
    while (text[i]!='\0')
    {
      printf ("%c", text [i]); // Affichage du caractère actuellement pointé
      i++;
    }
    prinf ("\n"); // Un simple retour chariot


    Voila un début pour te permettre d’appréhender la problématique. Rien qu'avec ce petit exemple de code tu devrais pouvoir t'en sortir. N'hésite pas à poster ton code pour qu'on puisse te dire comment avancer.
    Utilisation de Glade avec Gtk+ - N'oubliez pas de consulter les FAQ Gtk et les cours et tutoriels Gtk

  7. #7
    Nouveau Candidat au Club
    Ce point la je l'ai effectivement bien compris mais le point que je n'arrive pas à comprendre c'est quand je dois en fonction des caractères tapés (argv[1] ) trouvés dans le fichier les mots contenant ces caractères.

  8. #8
    Expert éminent
    Comme je le disais , si tu ne peux pas utiliser la bibliothèque string.h c'est pour ne pas utiliser les fonctions strcmp, strncmp, et petites sœurs (<- 2 liens cplusplus.com en anglais)

    Et donc, il faut faire 1 double boucle : 1 pour parcourir ton texte, et 1 pour comparer ton texte et le mot à rechercher.
    Regarde ce fil de discussion et mes 2 réponses : celle algorithmique et celle avec du code

    Et voici 1 code pour comparer 2 chaînes de caractères (pour ton cas , il faut modifier le test d'égalité strict sur les longueurs)
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
     
    /*****************************************************************************/
    /***********************************  Main  **********************************/
    /*****************************************************************************/
     
    unsigned is_the_same(char* str1, char* str2) {
        unsigned char ret;
     
    //  Test if (str1 != NULL) and (str2 != NULL)
     
        size_t len1 = strlen(str1),
               len2 = strlen(str2);
     
        if (len1 == len2) {
            size_t index;
     
            ret = 1;
     
            for(index=0; (ret & (index < len1)); ++index) {
                if (str1[index] != str2[index]) {
                    ret = 0;
                }
            }
        } else {
            ret = 0;
        }
     
        return ret;
    }
     
     
    int main(int argc, char** argv)
    {
        char str1[] = "degouts";
        char str2[] = "degoute";
     
        printf("\"%s\" & \"%s\" : %s\n", str1, str2, (is_the_same(str1, str2)? "same": "different") );
        printf("\"%s\" & \"%s\" : %s\n", str1, str1, (is_the_same(str1, str1)? "same": "different") );
     
        return EXIT_SUCCESS;
    }

  9. #9
    Expert éminent sénior
    Bonjour
    Citation Envoyé par foetus Voir le message
    Comme je le disais , si tu ne peux pas utiliser la bibliothèque string.h c'est pour ne pas utiliser les fonctions strcmp, strncmp, et petites sœurs
    Admettons. Mais cela interdit-il de les recréer ??? Après tout c'est la base de l'apprentissage que de réduire les difficultés. Et pour chercher une chaine dans un fichier, on peut alors commencer par chercher une chaine dans une chaine...

    Citation Envoyé par Clempost5 Voir le message
    Ce point la je l'ai effectivement bien compris mais le point que je n'arrive pas à comprendre c'est quand je dois en fonction des caractères tapés (argv[1] ) trouvés dans le fichier les mots contenant ces caractères.
    Imaginons que tu cherches "monde" dans un fichier contenant "Magellan a fait le tour du monde et n'en a pas vu le bout", comment ferais-tu avec un papier et un crayon ?
    Tu te places sur le premier caractère et tu regardes si les 5 caractères qui sont là ("Magel") sont "monde". Non. Ensuite tu te places sur le suivant et de là tu prends les 5 ("agell"). Puis le suivant ("gella") puis ("ellan") et etc etc jusqu'à arriver à "du mo" puis "u mon" puis " mond" et enfin tu arrives à "monde".
    Essaye déjà d'écrire une fonction qui reçoit une string "meule" et une string "aiguille" et qui regarde si "aiguille" se trouve dans "meule". Evidemment tu testes cette fonction dans toutes les configurations possibles.
    Une fois qu'elle est ok, ne te reste qu'à lire ton fichier ligne par ligne (on part du principe que la string ne peut pas être multi-lignes) et utiliser ta fonction pour tester si la string se trouve effectivement dans la ligne.
    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

  10. #10
    Membre averti
    Bonsoir,

    Citation Envoyé par foetus Voir le message
    Et après, il faut te poser 1 question : est-ce que ton mot à rechercher peut contenir les caractères de fin ligne '\n' (Line Feed) et/ ou '\r' (Carriage Return) ou pas ?
    • Si non, tu peux lire ton fichier ligne par ligne avec la fonction fgets (<- lien cplusplus.com en anglais)
    • Si oui (mais également non ), il faut charger ton fichier dans 1 buffer (en gros, c'est 1 grosse chaîne de caractères). Il me semble qu'il faut lire 1 fois ton fichier pour calculer le nombre de caractères (avec la fonction fgetc) allouer 1 buffer avec ce nombre de caractères, le remplir en relisant ton fichier, faire ta recherche et libérer la mémoire. Ouais, c'est nettement plus long
    Pour charger un fichier texte en mémoire, dans une chaîne, il n'est pas nécessaire de parcourir tout d'abord tout le fichier avec fgetc() pour pouvoir dimensionner le tampon.

    On peut utiliser fseek() pour déplacer l'indicateur de position à la fin du fichier et ftell() pour obtenir la valeur de l'indicateur de position, et on peut allouer le buffer correctement dimensionné avec malloc(). Ensuite, pour lire le fichier, on se remettra au début du fichier avec fseek() et on pourra lire tout le fichier d'un coup avec fread().

    Cela donne quelque chose comme cela (sans contrôles d'erreurs) :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    	/* open file and store it in a buffer */
    	FILE * fich = fopen("filename.txt", "rb");
    	fseek(fich, 0, SEEK_END);
    	long fsize = ftell(fich);
    	fseek(fich, 0, SEEK_SET);
    	char * buffer = malloc(fsize + 1);
    	fread(buffer, fsize, 1, fich);
    	fclose(fich);
     
    	/* terminate the buffer with '\0' so we have a valid C string */
    	buffer[fsize] = '\0';

  11. #11
    Expert éminent
    Citation Envoyé par Sve@r Voir le message
    Mais cela interdit-il de les recréer ??? Après tout c'est la base de l'apprentissage que de réduire les difficultés. Et pour chercher une chaine dans un fichier, on peut alors commencer par chercher une chaine dans une chaine...
    1. Si on interdit ces fonctions, c'est sûrement pour les recoder. Mais bon, c'est juste 1 bête parcours de chaîne de caractères
    2. Effectivement, on commence par apprendre l'existant pour le recoder.
    3. C'est pour cela que j'ai donné le lien d'1 autre fil de discussion qui avait ce sujet



    Citation Envoyé par -Eks- Voir le message
    On peut utiliser fseek() pour déplacer l'indicateur de position à la fin du fichier et ftell() pour obtenir la valeur de l'indicateur de position, et on peut allouer le buffer correctement dimensionné avec malloc(). Ensuite, pour lire le fichier, on se remettra au début du fichier avec fseek() et on pourra lire tout le fichier d'un coup avec fread().
    Effectivement tu as raison sauf que SEEK_END est dépendant de l'implémentation - voir la documentation de la fonction fseek (<- lien cplusplus.com en anglais)
    C'est sous Windows , il me semble, qu'on peut avoir des surprises. D'où mon approche de bourrin

    Je pense à 1 autre approche : allouer 1 tableau morceau par morceau (à coups de realloc) et ainsi plus la peine de connaître la taille du fichier. Seulement, on remplit le buffer.
    Par contre, c'est impossible de savoir à l'avance, si on ne connait pas la taille du fichier, la bonne taille des morceaux. Et donc soit
    1. avec des morceaux ayant 1 grande taille - avoir 1 grosse partie inutile à la fin de son buffer (dans le contexte de juste faire 1 recherche)
    2. avec des morceaux ayant 1 petite taille - faire trop de réallocations

  12. #12
    Membre averti
    J'avais omis cet aspect de SEEK_END, merci de l'avoir rappelé... Il y a des solutions dépendantes de l'implémentation ou du système pour obtenir la taille d'un fichier. Sur un système POSIX on pourra utiliser fstat() ou ftello(), et avec un système Windows, par exemple le GetFileSizeEx() de l'API Windows.

    realloc(), pour lire un fichier entier par morceaux avec fread(), évite de tomber dans des solutions dépendantes du système ou de l'implémentation.

    Je pense que c'est aussi ce qu'il faudrait faire pour lire une ligne en C standard pur, si on travaille ligne par ligne (sauf à utiliser des extensions GNU, etc.).

    Sur la taille des morceaux alloués successivement, que tu veuilles lire tout le fichier ou le lire ligne par ligne, tu as un autre facteur à prendre en compte : le temps pris les appels systèmes nécessaires aux lectures répétées. Utiliser un tampon d'une taille de BUFSIZ peut être une bonne idée pour que les I/O utilisent des morceaux d'une taille efficiente pour le système.

  13. #13
    Nouveau Candidat au Club
    Bonjour,

    Tout d'abord merci pour votre aide ! Je vous avoue que vous m'avez perdu sur certains points, pourriez vous m'expliquer ce que vous en pensez ? Car en faite je vous explique, je sais ouvrir un fichier et le lire avec fopen() etc en fonction du nom du fichier mit dans argv[2], ensuite ce que je n'arrive pas c'est en fonction de argv[1], trouver la ou les phrases qui correspondent à ma chaîne de caractère par exemple :

    J'ai un fichier qui contient ça et le nom de ce fichier est test :

    voiture
    vélo
    ligne
    chien
    caillou
    bateau à voile

    Ensuite sur l'invite de commande linux je dois taper ça

    ./grep(nom de mon programme) voi test

    et il doit me donné toutes les phrases qui contiennent ces 3 caractères :

    voiture
    bateau à voile

    Voilà je vous remercie de votre temps





    ca

  14. #14
    Expert éminent sénior
    Citation Envoyé par Clempost5 Voir le message
    ensuite ce que je n'arrive pas c'est en fonction de argv[1], trouver la ou les phrases qui correspondent à ma chaîne de caractère
    Oui tu l'as déjà dit. Donc est-ce tu l'as écrite la fonction dont je t'ai parlé ? Celle qui reçoit deux paramètres "aiguille" et "meule" de type char* et qui peut dire si oui ou non "aiguille" se trouve dans "meule" ???
    Parce que tant que tu ne l'auras pas écrite, tu ne pourras pas écrire ton grep. Alors ok tu peux aussi te dire "je m'en fouts de ce qu'il me raconte avec sa fonction à la con, j'ai qu'à faire ma recherche directement dans le fichier" sauf que malheureusement que ce soit fait dans une fonction séparée ou bien fait directement dans le fichier, l'algo de recherche restera le même (celui que je t'ai expliqué avec "monde" et "Magellan a fait..."). Donc si tu le faits dans une fonction séparée, les tests seront beaucoup plus simples à faire.

    Et puis arrête de te focaliser sur argv[1] et argv[2], ce ne sont que deux strings comme les autres.
    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

  15. #15
    Nouveau Candidat au Club
    Oui j'ai bien réalisé ce que tu m'as demandé j'espère qu'il est bon surtout :

    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
    #include <stdio.h>
    #include <stdlib.h>
     
    int main()
    {
        char text []= "Aiguille";
        char text1 [] = "meule";
        int i = 0; // index pour se déplacer dans la chaîne. 0 correspond à la première lettre de la châine.
        while(text[i] != '\0')
        {
            if(text[i]== text1[i])
            {
                printf("Les mots sont les memes ");
            }
            else
            {
                printf("Pas les memes ");
            }
            i++;
        }
    }

  16. #16
    Expert éminent sénior
    Citation Envoyé par Clempost5 Voir le message
    Oui j'ai bien réalisé ce que tu m'as demandé
    Déjà quand je te dis de faire ça dans une fonction, il faut le faire dans une fonction (sous-entendu "autre que main"). Parce qu'après, ce sera easy d'inclure la fonction dans le vrai grep (il n'y aura qu'à lire le fichier et appeler la fonction en y passant le mot à trouver et la ligne lue). Et bien entendu, la fonction ne doit rien afficher. Elle se contente de renvoyer un flag symbolisant "égaux/différents" et c'est l'appelant de la fonction qui décidera de quoi faire du résultat.

    Citation Envoyé par Clempost5 Voir le message
    j'espère qu'il est bon surtout :
    Ben... tu l'as testé ??? C'est aussi une étape importante du développement que de tester ses fonctions.
    Donc non c'est pas bon. Là tu compares chaque caractère, et pour chaque caractère égal à celui de l'autre chaine, tu dis que les mots sont les mêmes. Donc pour par exemple titi et tito, ça te dira 3 fois "les mots sont les mêmes" (ce qui n'est pas vraiment exact) pour finir par te dire qu'ils sont différents. Mais pour "voi" et "bateau à voile"...

    En fait j'ai l'impression que tu as essayé de refaire "strcmp()". C'est pas vraiment une mauvaise chose (encore que la vraie fonction qu'il faut ce serait plutôt "strncmp()"). Mais essaye de regarder la doc de "strstr()" et de faire quelques tests avec cette fonction. Et regarde aussi ce que j'explique concernant comment trouver "monde" dans "Magellan a fait le tour du monde et ..."

    PS: "aiguille" et "meule" sont des noms de variables, pas les chaines à tester => man strstr...
    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

###raw>template_hook.ano_emploi###