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 :

Projet récalcitrant "recherche de mot"


Sujet :

C

  1. #1
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut Projet récalcitrant "recherche de mot"
    Bonjour à tous,
    Je me suis replongé hier dans un vieux TP de C, et ne me suis couché qu'à 5h du matin... Confronté à un problême insoluble pour moi.
    J'ai donc à créer une commande "mgrep", qui a partir d'une suite de ligne (dans un fichier par exemple...) sur l'entrée standard, renvoie sur la sortir standard les lignes contenant le mot passé en paramêtre.
    Désolé de vous imposer tout ce code, j'ai tenté de le commenter un maximum (je ne mets pas ici les headers des fichiers (readl.c et fatal.c) :
    fatal.c : renvoie une erreur personnalisée :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
     
    /* 
    Envoie une erreur sur stderr
    si assert est different de 0
    */
    void fatal(int assert, const char* chaine, int status){
    	if(assert){
    		fprintf(stderr,"%s \n", chaine);
     
    		exit(status);
    	}
    }
    readl.c : Lit une ligne de 80 caracteres ou moins, renvoie la longueur de la chaine lue, ou "End Of File" si c'est le cas. Place la chaine dans le pointeur en parametre :
    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>
    #include "fatal.h"
    #include "const.h"
     
     
    int readl(char* line){
    	int i=0;
    	char c;
    	c=fgetc(stdin);
    	while (c!=EOF && c!='\n' && i<MAXLINE){
    		if(c!='\0'){
    			line[i++]=c;
    		}
    		c=fgetc(stdin);
    	}
    	fatal((i>=MAXLINE), "ligne trop longue (plus de 80 caracteres)", 1);
    	if(c==EOF){return(EOF);}
            if(c=='\n'){line[i]='\n';}
    	return i;
    }
    mgrep.c : et enfin la fonction main elle même :
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include "readl.h"
    #include "fatal.h"
    #include "const.h"
     
    /* Affiche sur la sortie standard, les lignes
    de la chaine ou du fichier de l'entree standard
    contenant le mot passe en parametre */
     
    int main(int argc, char *argv[]){
    	int booleanMotTrouve = 0;
    	char* word;
    	char ligne[MAXLINE];
    	int index;
    	int i;
    printf("1 \n");
    	if(argc==2){			
    		word=argv[1];		
    	}
    	else{				
    		fatal(1, "liste de parametres incorrecte", 2); 
    	}
    printf("2 \n");
    	while(readl(ligne)!= EOF){	
    		for(index=0; index<(readl(ligne)); ++index){ 
    			if (ligne[index]== *word){     
    				word++;
    				if(*word=='\0'){ 
     
    					booleanMotTrouve = 1;  
    					for(i=0; i<(readl(ligne)+1); ++i){
    						putc(ligne[i], stdout); 
    					}
    				}	
    			}
    		}
    	word=argv[1]; 
    	}
    printf("3 \n");
    	return (booleanMotTrouve==1?0:1);  
     
    }
    RQE : la constante MAXLINE, est définie dans un fichier "const.h" comme valant 81.

    Le projet compile parfaitement, donc l'erreur vient clairement d'un problême de conception de ma part...
    Lorsque je lance la commande mgrep, suivi d'un mot à chercher, puis d'un fichier sur l'entree standard, il ne se passe rien, et j'obtiens un nouvel invite de commande.

    EDIT : j'ai un peu modifié mon code, et à l'éxécution d'une commande type "./mgrep la < text" avec "text" un fichier de plusiseurs lignes, dont certaines contiennent "la", j'obients :
    1
    2
    3

    Merci à tout ceux qui prendront le temps de m'aider, et merci déjà d'avoir lu tout ça!

  2. #2
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Bonjour,
    Pour commencer, ton intention est-elle de refaire ce TP avec les mêmes restrictions qu'à l'époque ?
    En particulier au niveau des fonctions de la bibliothèque standard utilisables ou pas.
    Parce que si non, il y a largement moyen d'améliorer le code...

    Quoiqu'il en soit, le problème vient de la boucle while.
    Excuse-moi de le dire comme ça, mais elle est complètement foireuse...
    As-tu une idée du nombre de lignes lues dans une seule itération ?
    Je ne serais pas étonné que dans pas mal de cas, l'entrée standard soit complètement lue dès la première itération...

    Il faut savoir que, à moins de savoir exactement ce que l'on fait, c'est une mauvaise idée d'appeler une fonction avec des effets de bord dans la condition d'une boucle.
    Dans le meilleur des cas, les conséquences sont négligeables, voire ça fait ce que l'on veut ; dans le pire des cas, cela rend le programme incohérent.

    Lorsque tu appelles readl, tu lis une ligne de l'entrée standard, et par conséquent tu déplaces le curseur interne (le fameux effet de bord) ; de plus, tu modifies de contenu des données pointées par ligne.
    Lors d'un appel suivant, l'état de l'entrée standard a changé, et tu ne peux espérer obtenir le même résultat que précédemment.

    Pour en revenir aux boucles, tu dois savoir que la condition est évaluée à chaque itération.
    Alors si tu appelles readl dans la condition, elle sera exécutée à chaque itération, et comme je le disais juste au-dessus, aura un résultat potentiellement différent.
    Autrement dit, à chaque itération, une nouvelle ligne est lue depuis l'entrée standard (et donc le curseur interne est déplacé), le contenu de la chaîne ligne est modifié, et une valeur a priori différente est retournée.
    Tu commences à voir ce qui pose problème ?

    Décortiquons un peu ton code...
    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
    while(readl(ligne)!= EOF){  /* La première ligne de l'entrée standard est lue,
                                 * ligne contient la première ligne,
                                 * la longueur de la première ligne est retournée.
                                 */
        for(index=0; index<(readl(ligne)); ++index){  /* La deuxième ligne est lue à l'entrée dans la boucle,
                                                       * ligne contient la deuxième ligne,
                                                       * la longueur de la deuxième ligne est retournée.
                                                       * À chaque itération suivante, la ligne suivante est lue,
                                                       * ligne contient cette ligne,
                                                       * la longueur de cette ligne est retournée.
                                                       */
                (...)
                for(i=0; i<(readl(ligne)); ++i){  /* À l'entrée dans la boucle, et pour chaque itération, la ligne suivant la ligne courante est lue,
                                                   * ligne contient cette ligne,
                                                   * la longueur de cette ligne est retournée.
                                                   */
                    (...)
                }
            }
        }
    Toi qui t'attends à obtenir la même valeur pour toutes les itérations des boucles for...

    Il faut que tu t'assures de ne lire qu'une seule ligne par itération de la boucle [codeninline]while[/codeinline].
    Autrement dit, il faut que la fonction readl ne soit appelée qu'une fois par itération.
    Et pour cela, tu n'as pas le choix : utilise des variables auxiliaires (ligne en est déjà une ).


    Une fois que tu auras résolu ce problème, tu te heurteras à un autre.
    Hé oui, désolé...
    Ta façon de détecter qu'un mot est bien présent dans une ligne n'est pas bonne.
    En réalité, tu testes si tous les caractères du mot recherché apparaissent dans la ligne courante, et dans le même ordre.
    Par exemple, ton programme (corrigé) trouverait que le mot « toto » apparaît dans la ligne suivante :
    Nous allons tous ensemble fêter Noël !
    Bon courage.

  3. #3
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Citation Envoyé par Alba.1337 Voir le message
    EDIT : j'ai un peu modifié mon code, et à l'éxécution d'une commande type "./mgrep la < text" avec "text" un fichier de plusiseurs lignes, dont certaines contiennent "la", j'obients :
    1
    2
    3
    C'est exactement ce que je disais...
    Citation Envoyé par Steph_ng8 Voir le message
    Je ne serais pas étonné que dans pas mal de cas, l'entrée standard soit complètement lue dès la première itération...

  4. #4
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    Bon sang mais c'est bien sur!
    Merci beaucoup... J'y retourne (pis merci de ne pas m'avoir filé une réponse toute faire, je fais ça pour réviser, et je suis du genre feignasse...).
    Je posterai le résultat ici si ça marche, voir si je peux optimiser.

  5. #5
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Citation Envoyé par Alba.1337 Voir le message
    (pis merci de ne pas m'avoir filé une réponse toute faire, je fais ça pour réviser, et je suis du genre feignasse...)
    À ton service.
    Et puis, ce n'est pas la philosophie de la maison...

  6. #6
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    OUF! Merci, j'arrive enfin à un résultat qui commence à devenir intéressant...
    Voilà mes fichier .c à l'heure actuelle :
    readl.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
    #include <stdio.h>
    #include <stdlib.h>
    #include "fatal.h"
    #include "const.h"
     
     
     
     
    /*
    Lit une ligne et une seule sur l'entrée standard;
    retourne EOF, si la fin du fichier est atteinte,
    sinon, retourne le nombre de caracteres lus.
    Quitte sur une erreur si la chaine fait plus de 80 caracteres.
     
    Stocke dans "line" la ligne lue.
    */
    int readl(char* line){
        int i=0;
        char c;
        c=fgetc(stdin);
    /*printf("-r- %c\n", c);*/
        while (c!=EOF && c!='\n' && c!='\0' && i<MAXLINE){
            line[i++]=c;
            c=fgetc(stdin);
    /*printf("-r- %c\n", c);*/
        }
        fatal((i>=MAXLINE), "ligne trop longue (plus de 80 caracteres)", 1);
        if(c==EOF){return(EOF);}
        if(c=='\n'){line[i]='\n';}
        if(c=='\0'){line[i]='\0';}
     
        return i;
    }
    mgrep.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
    #include <stdio.h>
    #include <stdlib.h>
    #include "readl.h"
    #include "fatal.h"
    #include "const.h"
     
    /* Affiche sur la sortie standard, les lignes
    de la chaine ou du fichier de l'entree standard
    contenant le mot passe en parametre */
     
    int main(int argc, char *argv[]){
        int booleanMotTrouve = 0;
        char* word;
        char ligne[MAXLINE];
        int index;
        int i;
        if(argc==2){
            word=argv[1];
        }
        else{
            fatal(1, "liste de parametres incorrecte", 2);
        }
        i = (readl(ligne));
        while(i!= EOF){
            for(index=0; index<=i; ++index){
                if (ligne[index]== *word){
                    word++;
                    if(*word=='\0'){
     
                        booleanMotTrouve = 1;
                        printf("%s", ligne);
                    }
                }
                else {
                    word=argv[1];
                }
            }
        i=(readl(ligne));
        }
        return (booleanMotTrouve==1?0:1);
     
    }
    Bon comme je disais j'ai un résultat "intéressant", donc y'a encore un truc qui doit merdouiller...

    Quand j'appelle "./mgrep la < text > res", les fichiers sont :
    text :
    la neige est belle
    il fait beau
    il pleut
    il fait de la pelle
    il nage
    la peche c'est bien
    l'avion vole
    il fait du velo
    j'ai de la moustache
    RQE : Ca a été fait a 4h du mat' hein...

    res :
    la neige est belle
    r·il fait de la pelle
    ·la peche c'est bien
    ·j'ai de la moustache
    Et voilà!
    Si vous avez une idée de ce qui peut déconner...
    Merci en tout cas de votre aide!

  7. #7
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Chez moi, ce sont bien les lignes du fichier d'origine qui s'affichent, mais selon qu'il est terminé ou non par une ligne vide, j'ai la dernière ligne (« j'ai de la moustache ») et une ligne avec des caractères bizarres, ou je n'ai pas la dernière ligne.

    L'absence de la dernière ligne s'explique par le fait que lorsque tu arrives à la fin du fichier, readl renvoie EOF que la ligne soit vide ou non.
    Pour le reste, je ne vois pas très bien d'où ça peut venir.

    Sinon, fgetc retourne un [codeinline]int[/codinline].
    C'est pour différencier EOF des caractères valides ; de mémoire, sa valeur est -1.

    Une dernière chose.
    En général quand on fait un assert, c'est pour vérifier qu'une condition est vraie : si la condition est vraie, alors on continue le programme ; sinon, on arrête tout.
    En passant, il n'y a pas moyen de simplifier ceci (je suis sûr qu'on peut l'écrire en deux lignes... ) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        if(argc==2){
            word=argv[1];
        }
        else{
            fatal(1, "liste de parametres incorrecte", 2);
        }

    Sinon, es-tu conscient(e) que si un mot apparaît plusieurs fois dans une même ligne, cette ligne sera affichée autant de fois ?
    C'est peut-être voulu, mais je veux m'en assurer...


    Sinon, si ça t'intéresse, une fois que tu te seras satisfait(e) de ton programme, on pourra te montrer comment l'améliorer en utilisant les fonctions fournies par la bibliothèque standard...

  8. #8
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    Alors pour le "assert" dans le fatal, je propose à la place :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    fatal((argc!=2), "Nombre de parametres incorrect", 2);
    word=argv[1];
    Pour le "readl" et le EOF je bloque un peu...
    Comment traiter la dernière ligne normalement, même si celle-ci se termine par EOF, puis, lors de l'appel suivant, ne stocker que EOF, et renvoyer EOF...

    Ensuite, je veux bien les exemples d'utilisation des fonctions des bibliothèques standard si vous avez le temps!

  9. #9
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Citation Envoyé par Alba.1337 Voir le message
    Alors pour le "assert" dans le fatal, je propose à la place :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    fatal((argc!=2), "Nombre de parametres incorrect", 2);
    word=argv[1];


    Citation Envoyé par Alba.1337 Voir le message
    Pour le "readl" et le EOF je bloque un peu...
    Comment traiter la dernière ligne normalement, même si celle-ci se termine par EOF, puis, lors de l'appel suivant, ne stocker que EOF, et renvoyer EOF...
    Voyons voir.
    Tu as une variable qui indique le nombre de caractères lus depuis le début de la ligne.
    Je me demande si l'on ne pourrait pas l'utiliser...

    Lorsqu'une lecture depuis un flux détecte la fin du fichier, toutes les lectures suivantes ne « voient » que la fin du fichier.
    Tant que le flux n'a pas été fermé (puis rouvert), ou que le curseur interne n'a pas été déplacé...

    Citation Envoyé par Alba.1337 Voir le message
    Ensuite, je veux bien les exemples d'utilisation des fonctions des bibliothèques standard si vous avez le temps!
    Ok.
    Fais-moi signe quand tu auras fini avec ce programme.
    Mais prépare-toi, tu risques de voir ton code faire une cure d'amaigrissement...

  10. #10
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    J'me sens con là, je suis vraiment coincé pour le EOF...
    Je me vois bien faire une conditionnelle pour ne renvoyer que la chaine en cas de détection de OEF... Mais il me faudra bien renvoyer un OEF à l'appel suivant... Je vais quand même pas créer un booleen, incrémenté à la première détection de OEF, pour savoir quand je dois renvoyer OEF...

    Ca doit pas être très clair vu comme ça l'est pour moi...

    Sinon, je me sens d'avantage prêt à apprendre les fonctions des bibliothèques standards

    EDIT : Bien qu'en fait... Ca pourrait marcher non?

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include "fatal.h"
    #include "const.h"
     
     
     
     
    /*
    Lit une ligne et une seule sur l'entrée standard;
    retourne EOF, si la fin du fichier est atteinte,
    sinon, retourne le nombre de caracteres lus.
    Quitte sur une erreur si la chaine fait plus de 80 caracteres.
     
    Stocke dans "line" la ligne lue.
    */
    int readl(char* line){
        int i=0;
        int j=0;
        char c;
        c=fgetc(stdin);
        while (c!=EOF && c!='\n' && c!='\0' && i<MAXLINE){
            line[i++]=c;
            c=fgetc(stdin);
        }
        fatal((i>=MAXLINE), "ligne trop longue (plus de 80 caracteres)", 1);
        if(c==EOF){
            if (j==0){
                line[i]='\0';
                j++;
            }else{
                return(EOF); }
        }
        if(c=='\n'){line[i]='\n';}
        if(c=='\0'){line[i]='\0';}
     
        return i;
    }
    Bon bah moi ça a l'air de marcher, mais j'ai toujours en début de ligne, les caractères bizarres...

  11. #11
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    ... Bon je m'attaque au problème de la ligne qui sera écrite n fois si elle contient n fois le pattern recherché... Et en fait, j'en soulève un autre...

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include "readl.h"
    #include "fatal.h"
    #include "const.h"
     
    /* Affiche sur la sortie standard, les lignes
    de la chaine ou du fichier de l'entree standard
    contenant le mot passe en parametre */
     
    int main(int argc, char *argv[]){
        int booleanMotTrouve = 0;
        char* word;
        char ligne[MAXLINE];
        int index;
        int i;
        if(argc==2){
            word=argv[1];
        }
        else{
            fatal(1, "liste de parametres incorrecte", 2);
        }
        i = (readl(ligne));
        while(i!= EOF){
            for(index=0; index<=i; ++index){
                if (ligne[index]== *word){
                    word++;
                    if(*word=='\0'){
     
                        booleanMotTrouve = 1;
                        printf("%s", ligne);
                        /*word=argv[1];  //En effet, si un mot recherche terminait une ligne, et debutait la suivante, le pointeur sur "mot" ne pointait plus sur le premiere caractere du mot recherche*/
                        break; //permet de quitter l'analyse de la ligne si une occurrence du mot a ete trouvee, donc plus de doublon!
                    }
                }
                else {
                    word=argv[1];
                }
            }
        i=(readl(ligne));
        }
        return (booleanMotTrouve==1?0:1);
     
    }
    ---> Modifications ligne 32-33
    Mais bon, c'eut été trop beau... J'ai un résultat bizarre, avec des lignes avec juste des "a" en recherchant toujours "la"...

    première modif annulée, ce cas de figure ne peut pas se présenter puisque la ligne se termine par \n ou \o

  12. #12
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Citation Envoyé par Alba.1337 Voir le message
    J'me sens con là, je suis vraiment coincé pour le EOF...
    Je me vois bien faire une conditionnelle pour ne renvoyer que la chaine en cas de détection de OEF... Mais il me faudra bien renvoyer un OEF à l'appel suivant... Je vais quand même pas créer un booleen, incrémenté à la première détection de OEF, pour savoir quand je dois renvoyer OEF...
    C'est une idée.
    Mais ça ne fonctionnera pas si tu ne déclares pas j comme étant static.
    Si tu ne sais pas ce qu'est une variable statique : F.A.Q. C: Que signifie le mot-clé static ?
    Ceci dit, on peut faire sans...

    Citation Envoyé par Alba.1337 Voir le message
    ... Bon je m'attaque au problème de la ligne qui sera écrite n fois si elle contient n fois le pattern recherché... Et en fait, j'en soulève un autre...
    Ah, tu y es presque.
    Tout ce qu'il faut, c'est qu'au moment où tu commences la recherche du mot, word pointe bien sur le début du mot.
    Je ne peux pas te donner plus d'indices...

    Citation Envoyé par Alba.1337 Voir le message
    Sinon, je me sens d'avantage prêt à apprendre les fonctions des bibliothèques standards
    Comme tu veux.
    Je n'ai pas le temps dans l'immédiat, mais je te ferai ça ce soir ou demain.

  13. #13
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    mgrep.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
    #include <stdio.h>
    #include <stdlib.h>
    #include "readl.h"
    #include "fatal.h"
    #include "const.h"
     
    /* Affiche sur la sortie standard, les lignes
    de la chaine ou du fichier de l'entree standard
    contenant le mot passe en parametre */
     
    int main(int argc, char *argv[]){
        int booleanMotTrouve = 0;
        char* word;
        char ligne[MAXLINE];
        int index;
        int i;
        if(argc==2){
            word=argv[1];
        }
        else{
            fatal(1, "liste de parametres incorrecte", 2);
        }
        i = (readl(ligne));
        while(i!= EOF){
            for(index=0; index<=i; ++index){
                if (ligne[index]== *word){
                    word++;
                    if(*word=='\0'){
     
                        booleanMotTrouve = 1;
                        printf("%s", ligne);
                        /*word=argv[1];  //En effet, si un mot recherche terminait une ligne, et debutait la suivante, le pointeur sur "mot" ne pointait plus sur le premiere caractere du mot recherche*/
                        break; //permet de quitter l'analyse de la ligne si une occurrence du mot a ete trouvee, donc plus de doublon!
                    }
                }
                else {
                    word=argv[1];
                }
            }
        i=(readl(ligne));
        word=argv[1];  /* On remet le pointeur de word au debut, a chaque debut de ligne, donc debut de recherche */
        }
        return (booleanMotTrouve==1?0:1);
     
    }
    Voilà pour les doublons!

    Et donc pour readl.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
    #include <stdio.h>
    #include <stdlib.h>
    #include "fatal.h"
    #include "const.h"
     
     
     
     
    /*
    Lit une ligne et une seule sur l'entrée standard;
    retourne EOF, si la fin du fichier est atteinte,
    sinon, retourne le nombre de caracteres lus.
    Quitte sur une erreur si la chaine fait plus de 80 caracteres.
     
    Stocke dans "line" la ligne lue.
    */
    int readl(char* line){
        int i=0;
        int j; 
        char c;
        c=fgetc(stdin);
        while (c!=EOF && c!='\n' && c!='\0' && i<MAXLINE){
            j=0;
            line[i++]=c;
            c=fgetc(stdin);
        }
        fatal((i>=MAXLINE), "ligne trop longue (plus de 80 caracteres)", 1);
        if(c==EOF){
            if (j==0){
                line[i]='\0';
                j++;
            }else{
                return(EOF); }
        }
        if(c=='\n'){line[i]='\n';}
        if(c=='\0'){line[i]='\0';}
     
        return i;
    }
    Je n'ai pas utilisé de "static", car que si j'avais déclaré "static int j=0;" au début du fichier, elle aurait été réinitialisé à 0 à chaque appel de "readl"... Donc j'étais obligé de lui attribuer la valeur 0, là ou je l'ai fait, et pour cela, je n'avais pas besoin de "static"...
    Mais j'aimerais connaitre la solution avec un static s'il vous plait.

  14. #14
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Voilà...
    J'ai tout mis dans un seul fichier, que j'ai mis en pièce jointe pour le cas où tu voudrais préserver la surprise...
    Fichiers attachés Fichiers attachés

  15. #15
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    Ah merci! C'est quand même beau un code propre!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    ligne[i] = '\0';
        /* i vaut la longueur de la chaîne. * /
     
        /* On atteint la fin du fichier, sans lire de caractère avant. */
        if ((c == EOF) && (i == 0))
            return EOF;
    Cette portion de code me parait en effet plus naturelle, surtout maintenant que je sais que le code ascii de '\0' est 0
    C'était l'occasion de me faire un petit executable pour retrouver le code ascii d'un caractère passé en paramètre!

    ===> Nouveau problême sauvage apparait :

    Je veux donc coder un tout petit executable qui prend en parametre un caracère, et renvoie sa valeur entière...
    J'avais pondu ceci, et apparemment c'est plus compliqué que je ne le pensais :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(int argc, char* argv[]){
     
    	if (argc<2){
    		printf("Erreur ==> Nombre de parametres insuffisant");
    		exit(1);
    	}
    	char c;
    	c=(argv[1][0]);
    	int i= ((int) c);
     
    	printf("%d \n", i);
    	return 0;
     
    }
    Erreur à la compilation :
    gcc -o test.o -c test.c -Wall -ansi -pedantic
    test.c: In function ‘main’:
    test.c:10:2: warning: ISO C90 forbids mixed declarations and code
    test.c:12:2: warning: ISO C90 forbids mixed declarations and code
    Question :
    Un tel code, si d'aventure il venait à marcher, ne me donnerait la valeur numérique que du premier charactère du String en paramètre... Comment alors connaitre la valeur de, par exemple '\0' ou EOF...?

    Merci en tout cas!!

  16. #16
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    - le ISO C90 exige que les variables soient déclarées en début de bloc {}.
    Déplace les définitions de c et i au début de la fonction.

    -
    Comment alors connaitre la valeur de, par exemple '\0' ou EOF...?
    Pour '\0', il suffit que la chaine soit vide pour que ce soit le premier caractère
    Pour EOF, la question ne se pose pas en ces termes puisque ce n'est pas un caractère mais un int (de valeur <0)

  17. #17
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    Ah bah, oui j'ai plus mon erreur, merci!

    Mais du coup y'a pas vraiment de moyen de connaitre la valeur de '\n'? ou "espace" par exemple??

  18. #18
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include  <stdio.h>
    int main(void)
    {
         printf("'\\0' = %d\n" ,'\0');
         printf("' ' = %d\n"   , ' ');
         printf("'\\n' = %d\n" , '\n');
         printf("EOF = %d\n"   , EOF);
         return 0;
    }

  19. #19
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Pour connaître le code ASCII d'un caractère particulier, soit tu cherches une table ASCII (Table ASCII.com, par exemple), soit tu le transtypes en int (comme tu l'as fait dans ton dernier programme) :
    Ceci dit, tu auras une valeur négative pour les caractères dont le code ASCII est dans l'intervalle [128, 256).
    Dans ce cas, il faut d'abord transtyper en unsigned char (c'est le comportement des fonctions de lecture de flux caractère par caractère) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("%u\n", (unsigned char) 'à');
    Attention, si ton fichier source n'est pas encodé en ASCII (ou peut-être en ISO-8859-1, Latin1), le comportement du programme avec cette ligne ne sera pas celui attendu.

    Pour en revenir au programme que tu proposes, on peut l'améliorer un peu :
    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
    #include <stdio.h>
     
    int main(int argc, char *argv[])
    {
        char const *c;  /* Pointeur sur le caractère à afficher */
        char const **p; /* Pointeur sur la chaîne de caractères traitée */
     
        for (p = argv + 1; *p != NULL, ++p) {
            printf("\"%s\"\n", *p);
     
            for (c = *p; *c != '\0'; ++c) {
                if (*c == '\\') /* Séquence d'échappement. On s'intéresse donc au caractère suivant. */
                    switch (*++c) {
                        case 'a':   /* Alarme */
                            printf("'\\a': %03u\n", (unsigned char) '\a');
                            break;
     
                        case 'b':   /* Retour arrière */
                            printf("'\\b': %03u\n", (unsigned char) '\b');
                            break;
     
                        case 'f':   /* Saut de page */
                            printf("'\\f': %03u\n", (unsigned char) '\f');
                            break;
     
                        case 'n':   /* Nouvelle ligne */
                            printf("'\\n': %03u\n", (unsigned char) '\n');
                            break;
     
                        case 'r':   /* Retour charriot */
                            printf("'\\r': %03u\n", (unsigned char) '\r');
                            break;
     
                        case 't':   /* Tabulation horizontale */
                            printf("'\\t': %03u\n", (unsigned char) '\t');
                            break;
     
                        case 'v':   /* Tabulation verticale */
                            printf("'\\v': %03u\n", (unsigned char) '\v');
                            break;
     
                        case '\0':  /* Le dernier caractère de la chaîne est un '\\' littéral. Ce n'est donc pas une séquence d'échappement. */
                            --c;    /* On met le pointeur à jour pour que la fin de la chaîne soit détectée à la prochaine itération */
                        case '\\':
                            printf("'\\\\': %03u\n", (unsigned char) '\\');
                            break;
     
                        default:    /* Je ne pense pas avoir oublié de séquence... */
                            printf("'%c' : %03u\n", *c, (unsigned char) *c);
                            break;
                    }
     
                else
                    printf("'%c' : %03u\n", *c, (unsigned char) *c);
            }
     
            putchar('\n');
        }
    }
    Ainsi, on affiche le code ASCII de tous les caractères de chaque argument (sauf pour '\0', mais bon...).
    On peut même mettre des séquence d'échappement ('\n', '\t', etc.) ; sauf celles qui représentent directement un code ASCII (en décimal, hexadécimal ou octal).
    Mais la modification ne serait pas trop compliquée...

    Bon, pour les caractères non-imprimables, on aura peut-être quelques surprises (pour leur affichage).
    Quant aux caractères dont le code ASCII n'est pas dans [0, 128), et tous les caractères « exotiques » (lettres grecques, arabes, caractères asiatiques...), le résultat variera selon l'encodage de la console dans laquelle est lancée l'application.

    PS: Pour certains, ce n'est pas propre (c'est crade ?) de modifier la valeur du compteur d'une boucle for à l'intérieur de la boucle.
    Pour un petit exemple comme celui-là, je me suis autorisé cette digression, mais transformer cette boucle en boucle while est trivial.


    PS2: Si tu n'es pas à l'aise avec ma manière d'itérer à travers la liste des arguments ou à travers une chaîne de caractères, tu peux utiliser les indexes comme habituellement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int i, j;
     
    for (i = 1; i < argc; ++i) {
        printf("\"%s\"\n", argv[i]);
        for (j = 0; argv[i][j] != '\0'; ++j) {
            (...)
        }
    }
    PS3: Si aucun argument n'est fourni au programme, il ne fera rien.
    Je n'ai donc pas besoin de vérifier leur nombre avant de commencer.



    Edit: grilled...

  20. #20
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2010
    Messages
    119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2010
    Messages : 119
    Par défaut
    Je fais que passer chez moi, j'y jetterai un oeil ce soir, ou demain matin.
    Mais merci beaucoup a vous de votre aide!
    Je progresse plus en 2 jours qu'en un semestre complet...

Discussions similaires

  1. Rechercher enregistrement avec ' (quote)
    Par jpo dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 02/08/2007, 17h54

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