Forum Programmation.c Chaine de caractère

Posté par  .
Étiquettes : aucune
0
18
mai
2006
Bonjour,

j'ai peu de connaissance en C et beaucoup de problèmes avec les "chaines" de caractères et le C!
J'ai un fichier en entrée que je dois éclater en plusieurs fichiers.
entrée.txt =
DEBUT nomfichier1
infodiverses1
FIN
DEBUT nomfichier2
infodiverses2
FIN
etc...
le but évident étant d'avoir un fichier nommé nomfichier1.txt contenant
infodiverses1, un fichier nommé nomfichier2.txt contenant
infodiverses2 etc...

Je lis donc mon fichier entrée.txt avec fgets tant que je ne suis pas à la fin.
[code]
chainelu char[30];
char *ptr;
fgets(cjainelu,sizeofchainelu),entree.txt);
indice=str_istr (chainelu, " "); /* retourne l'indice de l'occurence " "*/
ptr=&chainelu[indice];
strcat(str,".txt");
[/code]
Problème :
C'est que ptr contient nomfichier1 avec des espaces en fin!!!!!! donc l'ajout n'amene rien.
J'ai essayé de détecter les espaces mais cela ne fonctionne pas.
Alors j'essaye d'extraire la chaine mais je trouve cela compliqué pour pas grand chose.
Pouvez m'aider en m'indiquant pourquoi il se comporte ainsi, comment faire pour simplifier , voir comment faire autrement si une solution plus simple existe et que je me fourvoye dans l'utilisation de C ?

Merci par avance,
C
  • # Utiliser glib ....

    Posté par  . Évalué à 2.

    La bibliothèque glib contient beaucoup de fonctions toutes prêtes pour gérer les chaînes de caractères et elle doit sûrement déjà être installée sur ton PC (suivant la distrib que tu utilises, il peut manquer les paquets de developpement).

    voir http://developer.gnome.org/doc/API/2.0/glib/index.html/
    pour la documentation de l'api (en anglais malheureusement)
    notamment la partie "String utility fonction".
  • # Ton post est une blague ou

    Posté par  . Évalué à -5.

    ... il faut vraiment que tu lises http://www.gnurou.org/Writing/SmartQuestionsFr d'urgence ? Nan parce qu'en voyant le code, j'ai l'impression d'halluciner.

    Bon, je vais quand même répondre parce qu'une solution en shell tient en 8 lignes :
    $ cat en_shell
    #! /bin/sh
    while read s; do
    case "$s" in
    DEBUT\ *) exec 3> "${s:6}.txt" ;;
    FIN) exec 3>&- ;;
    *) echo "$s" >&3 ;;
    esac
    done
    Ex. d'utilisation : ./en_shell < entrée.txt

    Et si tu veux des pistes pour le faire en C, montre que ça t'intéresse un minimum.
    • [^] # Re: Ton post est une blague ou

      Posté par  (site Web personnel) . Évalué à 2.

      Merci de respecter les gens qui débutent petit malin...
      C'est super que tu saches le faire, mais n'en dégoute pas les autres. T'as peut être appris le C au berceau, c'est pas le cas de tout le monde.
      M'énerve...
      • [^] # Re: Ton post est une blague ou

        Posté par  (site Web personnel) . Évalué à 3.

        1. commencer par écrire du pseudo code: tu veux faire quoi, dans quel ordre
        2. chercher les fonctions qui peuvent t'aider à le faire... notamment c'est bien le diable s'il n'y a pas une fonction qui lit une ligne entière dans un fichier texte
        3. oui, effectivement, le C de base, pour traiter des chaines de caractère de base, c'est tout pourri. Les langages de script comme python, perl et shell l'enfoncent.
        4. Il te reste la solution la plus simple: au lieu de réécrire la roue utiliser une API qui a des fonctions d'n peu plus haut niveau un peu plus conviviales, comme l'excellente glib, effectivement: http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html (regarde bien g_get_file_contents)
        5. ne pas se décourager, c'est pas évident quand on débute
        6. Commenter son code, mais pas comme tu le fais: dire *ce qu'on fait* et pas *comment on le fait* dans tes commentaires

        Si tu ne veux pas utiliser de bibliothèque comme la glib, alors tu trouveras une bonne référence des fonctions standard en C:
        http://www.delorie.com/djgpp/doc/libc-2.02/
        Les modules qui t'intéressent: stdio (pour l'accès au fichiers dans ons cas), string (pour la manipulation de chaines de caratères)

        après, tu fais plusieurs erreurs dans ton code, à vue de nez:

        chainelu char[30]; il faut mettre le type avant le nom de variable: char chainelue[30];
        char *ptr; ok
        Ensuite tu as besoin d'ouvir ton fichier pour récupérer un descripteur de fichier à utiliser dans fgets (regarde fopen: http://www.delorie.com/djgpp/doc/libc-2.02/libc_299.html ), du genre fd = fopen(entree.txt, "r");
        fgets(cjainelu,sizeofchainelu),entree.txt); => mauvais usage de sizeof, et on passe un pointeur sur un descripteur de fichier en dernier argument, et pas le nom du fichier :fgets(chainelue, sizeof(chainelue), fd);

        [...]

        Bref, relis un petit peu tes cours, commence par un ou deux tutoriels de C, parce que là tu manques cruellement de connaissances de base pour faire ce que tu souhaites. N'oublie pas aussi d'écrire ligne par ligne et de compiler juste après chaque ajout, les messages du compilateur te guideront pour corriger tes erreurs. Mais là tu es un peu "juste".
    • [^] # Re: Ton post est une blague ou

      Posté par  . Évalué à 2.

      Bonjour,

      Si tu trouves que la personne s'est mal exprimée, il suffit simplement de lui demander des détails et une explication un peu plus claire.
      Mais de là à lui envoyer un code à la figure et de lui dire ses quatres vérités c'est un peu fort.

      Il y en a qui découvre les languages.
      Surtout le C, qui n'est pas un des plus faciles.

      Donc la moindre des gentillesses aurait été de lui dire par exemple ... "Il existe une solution plus simple pour traiter ton problème ... un script bash ...".

      Voilà un bel exemple de la communauté et de l'entraide :|
    • [^] # (réponse groupée)

      Posté par  . Évalué à 3.

      Je veux bien mettre la forme de côté, mais franchement, j'ai beau chercher, je ne vois pas comment on peut en arriver à poster un bout de code pareil.

      Ne me dites pas que c'est le résultat auquel il est arrivé, ou le reste de son message aurait été complètement différent : il donne l'impression de l'avoir compilé alors pourquoi ne pas avoir _copié-collé_ ?

      Vous vous rendez compte que vous avez pris la peine de répondre ceci :
      "chainelu char[30]; il faut mettre le type avant le nom de variable: char chainelue[30];" ?

      Comme vous voulez, mais je pense qu'il gagnera à mieux poser ses questions. Et ce n'est pas comme si c'était son premier post.
      Ca n'a rien à voir avec le fait qu'il débute en C.

      voir comment faire autrement si une solution plus simple existe et que je me fourvoye dans l'utilisation de C ?
      Je comprends qu'il ne voulait pas forcément faire ça en C.

      Si vous préférez que j'ignore ce genre de questions, dites-le.
  • # Un peu de C...

    Posté par  . Évalué à 1.

    Je suppose que l'algorithme est déjà bien défini (traitement d'erreurs, notemment).

    Pour le codage en C: fgets sert à lire une ligne de texte. Mais attention, cette fonction n'est pas simple à utiliser. Elle s'arrête soit:
    - quand le marqueur de fin de ligne '\n' est rencontré
    - quand le buffer que tu lui as passé est plein
    - en cas d'erreur interne (I/O, par exemple).
    Dans le troisième cas, fgets renvoie NULL; pour différencier les deux premiers, il faut chercher le caractère '\n' dans le buffer (pour chercher un caractère dans une chaîne: strchr). Si la ligne n'est pas complète, il vaut mieux réallouer un buffer plus grand.

    Et tout ça est C ISO. Il n'y a pas besoin de dépendre d'une bibliothèque externe, mais il faut avouer que ça peut être utile; Pour lire une ligne d'un fichier sans s'embêter avec fgets/strchr/realloc, tu peux utiliser des choses déjà existantes, comme la fonction fggets de CB Falconer, un participant régulier de comp.lang.c, qui évite tous ces problèmes http://cbfalconer.home.att.net/download/index.htm
    . Cette fonction (implementée en C ISO, donc portable), distribuée en domaine public, t'épargne les tests à n'en plus finir. Tu auras juste à écrire quelque chose comme :
    char *ligne=NULL;
    FILE *fd = fopen("ton_fichier", "r");
    if (fd == NULL)
    { /* traitement d'erreur */ }
    while (fggets(&ligne, fd) == 0)
    {
    /* la boucle principale */
    }


    Ensuite, pour comparer deux chaînes (dans le cas de 'DEBUT' et 'FIN'): strcmp.

    Avec ça, tu devrais déjà avoir de quoi faire. Si tu as d'autres questions, ou que l'explication n'est pas claire, n'hésite pas à redemander.
  • # ma solution

    Posté par  . Évalué à 1.

    Me faisais chier et j'trouvais ça marrant ... dslé ...
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main(int ac, char **av)
    {
            char buf[BUFSIZ];
            FILE *f_in;
            FILE *f_out[100];
            int cpt=0, i;
            char *file, char_cpt[32];
    
            f_in = fopen("in", "r");
    
            file = strdup("out1");
            f_out[cpt] = fopen(file, "w+");
    
            while((fscanf(f_in, "%s", &buf)) != EOF)
            {
                    fprintf(f_out[cpt], "%s", buf);
                    fputc('\n', f_out[cpt]);
    
                    if(strncmp(buf, "FIN", 3) == 0)
                    {
                            free(file);
                            cpt++;
                            file = malloc(sizeof(char) * strlen("out") + cpt);
                            strcpy(file, "out");
                            snprintf(char_cpt, 32, "%d", cpt + 1);
                            strcat(file, char_cpt);
                            f_out[cpt] = fopen(file, "w+");
                    }
    
            }
            for(i=0;i<cpt;i++)
                    fclose(f_out[i]);
            free(file);
            fclose(f_in);
    
            return 0;
    }
    
    Ya surement moyen de faire mieux et plus commenté, mais ça a le merite de marcher ... toutes suggestions sont bienvenues
    • [^] # Re: ma solution

      Posté par  . Évalué à 1.

      int main(int ac, char **av)

      Si tu n'utilises pas les arguments, un void suffit.

      f_in = fopen("in", "r");

      Pense à vérifier si fopen a bien marché.

      file = strdup("out1");

      strdup est une extension GNU (je n'ai rien contre les extensions, c'est juste pour signaler qu'il y pourra y avoir des problèmes de portabilité par la suite).
      Et vérifie aussi que la fonction a bien marché, sinon le fopen avec une valeur nulle causera un comportement indéfini.
      f_out[cpt] = fopen(file, "w+");

      Idem: vérifier le succès de fopen

      while((fscanf(f_in, "%s", &buf)) != EOF)
      {
      fprintf(f_out[cpt], "%s", buf); fputc('\n', f_out[cpt]);

      Tu as oublié de comparer le début de buf avec "DEBUT". Et si une ligne du fichier d'entrée est plus longue que BUFSIZ, tu tronques les lignes...
      De plus, si la ligne comprend un expace au plein milieu, ton fscanf ne prendra pas la fin de la ligne.

      {
      free(file);
      cpt++;
      file = malloc(sizeof(char) * strlen("out") + cpt);
      strcpy(file, "out");
      snprintf(char_cpt, 32, "%d", cpt + 1);
      strcat(file, char_cpt);
      f_out[cpt] = fopen(file, "w+");
      }

      Tu pourrais rajouter le fclose dans ce block, pour éviter d'avoir un nombre indéterminé de fichiers ouverts en même temps (ça évite de planter si on ouvre trop de fichiers...).
      Pour ton malloc: sizeof(char) vaut 1 par définition, donc tu peux simplifier l'écriture en
      file = malloc(strlen("out") + cpt);

      Pour la première étape, on a cpt == 1, donc tu réserves 4 caractères (car strlen("out") == 3). Or, tu places ensuite la chaîne "out", puis la chaîne de caractères représentant cpt+1 en décimal, c'est-à-dire "2". Tu aboutis donc à 5 caractères: 'o', 'u', 't', '2', '\0'.
      Tu as donc effectué un débordement de tampon, et provoqué un comportement indéfini.
      Mais, pour cpt > 1, ta formule marche efefctivement. Note quand même que faire le + cpt réserve plus de caractères qu'il n'en faut. Peut-être as-tu fait exprès, sachant que cpt caractères sont toujours suffisants pour afficher le nombre cpt+1 (si cpt > 1...), mais il faudrait au moins le metre en commentaire.
      La ligne
      file = malloc(strlen("out") + cpt + 1);
      est donc suffisante; mais il ne faut pas oublier qu'on prend plus de place mémoire que nécessaire...
      Par flemme, on va dire que la réécriture de ce code pour ne pas prendre plus de mémoire que nécessaire est laissé en exercice au lecteur ;)
      Par ailleurs, il serait bon de vérifier que malloc n'a pas échoué, à moins de tenir à tester les comportements indéfinis de ton implémentation ;)
      Et il faudrait aussi noter que le "32" pour le buffer du sprintf est purement arbitraire (très largement suffisant, mais juste pour noter en commentaire dans le code).
      • [^] # Re: ma solution

        Posté par  . Évalué à 1.

        Merci pour toutes ces précisions :)

        Pour les tests, c'était plus de la flemme qu'autre chose.
        Pour fscanf, je l'utilise très rarement (jamais ?) donc je connais pas très bien son comportement, j'avoue ne pas avoir poussé les tests, j'me suis contenter de son exemple. J'avais mis un fgets, mais yavais de pb :/

        Pour le reste, j'en prend note.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.