Forum Programmation.c Un petit problème avec mon programme C

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
4
déc.
2015

Bonjour,
Je rencontre un souci avec mon programme.
Il s'agit d'un programme qui doit lire une suite d'adresses contenu dans un fichier.

Le fichier doit contenir par exemple:
5 rue Paul Froment Paris 75001
10 rue Paul Valery Lyon 65000
2 allée Pierre Gaspard Toulouse 75001

Toutes ces adresses sont enregistrées dans le fichier de manière verticale sur une seule colonne de façon à faciliter le programme pour leur lecture.

Voici mon programme:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct adresse Adresse;
struct adresse {char *num; char *voie; char *nomVoie; char *ville; char *codPostale;};

int main(void) {
int n = 0;
int i;

struct adresse *Adresse = NULL;

    FILE *f = fopen("/home/fichier.txt","r");
    if (!f) {
        printf("Impossible d'ouvrir le fichier\n");
        return 1;
    }
    char ligne[256];
    while (fgets(ligne,256,f)) {
        Adresse = realloc(Adresse, sizeof(struct adresse)*(n+1));
    if (!Adresse) {
         printf("out of memory\n");
         exit(1);
    }
Adresse[n].num = malloc( 256*sizeof( char));
Adresse[n].voie = malloc( 256*sizeof( char));
Adresse[n].nomVoie = malloc( 256*sizeof( char));
Adresse[n].ville = malloc( 256*sizeof( char));
Adresse[n].codPostale = malloc( 256*sizeof( char));

fscanf( f , "%s %s %s %s %s", Adresse[n].num, Adresse[n].voie, Adresse[n].nomVoie, Adresse[n].ville ,Adresse[n].codPostale);
n++;  
    }
  fclose(f);
    for (i = 0; i < n; i++)
    {
     printf("\nNuméro : %s \n", Adresse[i].num);
     printf("\nVoie : %s \n", Adresse[i].voie);
     printf("\nNom de la voie :%s \n", Adresse[i].nomVoie);
     printf("\nVille :%s \n", Adresse[i].ville);
     printf("\nCode postale : %s \n", Adresse[i].codPostale);

    }
    return 0;
}

Le programme compile bien mais malheureusement à l'affichage j'ai un problème au niveau du nom de la voie lorsqu'il contient plusieurs mots comme Paul Froment par exemple pour la première adresse. Du coup il y a un décalage :
Numéro: 5
Voie: rue
Nom de la voie: Paul
Ville: Froment
Code postale: Paris
Numéro: 75001
Voie: 10
……
Comment peux-je regler ce souci ?

Merci d'avance !

  • # Séparateur

    Posté par  . Évalué à 3. Dernière modification le 04 décembre 2015 à 19:41.

    Il te faut un séparateur afin de détermines les limites de chacun des champs.
    Sinon, comment veux tu que ton programme sache comment séparer les morceaux d'une adresse dans laquelle le numéro de voie peux contenir plusieurs parties (42 bis), de même que l'adresse (rue du Logiciel Libre) ou encore la ville (La Baule).

    Le plus "simple" est probablement de considérer un format de type CSV, ou la virgule (ou un autre caractère spécial) fait office de séparateur.

    Bon, en réalité, c'est loin d'être simple, je peste régulièrement contre mes collègues qui écrivent des parseurs "CSV-light" à la main, et qui ne fonctionne pas parce qu'ils ont oublié de considérer que les champs peuvent eux aussi contenir le séparateur en question, des guillemets, des sauts de lignes… Bref, réfléchis bien au contenu de tes champs.

    Pour parser un tel fichier, regarde du coté de strtok().

    • [^] # Re: Séparateur strtok()

      Posté par  . Évalué à 1.

      Merci pour ta réponse.

      Justement j'avais pensé à strtok():

      Adresse[n].num = strtok(ligne,"\t");
      Adresse[n].voie = strtok(ligne,"\t");
      Adresse[n].nomVoie = strtok(NULL,"\t");
      Adresse[n].ville = strtok(NULL,"\t");
      Adresse[n].codPostale = strtok(NULL,"\n");

      Je l'ai inseré à la place du scanf pour parser chaque ligne mais malheureusement je sais pas ou ça bloque car l'affichage n'est pas du tout délicat.

      Exemple de l'affichage:

      Numéro : 75019
      Voie : 75019
      Nom de la voie :(null)
      Ville :(null)
      Code postale : (null)
      Numéro : 75019
      Voie : 75019
      Nom de la voie :(null)
      Ville :(null)
      Code postale : (null)
      …..

      Si tu pourrais m'aider à corriger mon erreur.
      Merci d'avance !!

      • [^] # Re: Séparateur strtok()

        Posté par  . Évalué à 2.

        C'est un peu tricky à utiliser strtok au premier abord.
        Sans plus de détails sur ton code, je ne peux pas t'aider.
        Mais il y a plein d'exemples sur le net, et tu as l'air d'être sur la bonne voie.

    • [^] # Commentaire supprimé

      Posté par  . Évalué à 2.

      Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: Séparateur

        Posté par  . Évalué à 0.

        Dans mon cas, je ne cherche pas à généraliser. Ce n'est que le nom de la voie qui prévoit des espaces, déjà il faudrait que j'arrive à régler ce problème moins délicat que la généralisation.

        • [^] # Re: Séparateur

          Posté par  . Évalué à 3.

          Ce n'est que le nom de la voie qui prévoit des espaces

          quid des noms de villes composées ?
          Saint Jean de la Porte
          Saint Martin sur XYZ
          Sainte Marguerite

        • [^] # Commentaire supprimé

          Posté par  . Évalué à 2.

          Ce commentaire a été supprimé par l’équipe de modération.

  • # CSV

    Posté par  . Évalué à 2.

    Pourquoi vouloir réinventer le format CSV ?

    Il est standard, très répandu et justement conçu pour faciliter le traitement informatique des données de ce type.

    https://fr.wikipedia.org/wiki/Comma-separated_values

    • [^] # Re: CSV

      Posté par  . Évalué à 4.

      Parce que c'est un exercice universitaire. Il faut savoir faire les choses depuis grattage avant d'utiliser des outils facilitant la vie.

      • [^] # Re: CSV

        Posté par  . Évalué à 1.

        Justement, tu as tout juste Joalland. Pourrais tu me suggerer une solution ? ;)

  • # backward....

    Posté par  . Évalué à 1.

    Ploup,

    Le problème n'est pas un problème de "parse", mais un problème d'algorithme de parse.
    Il peut être renommé en comment "parser une ligne, ou j'ai 2 champ sans espace au début, 2 champs sans espace à la fin, et un champ avec espace au milieu ?"

    L'une des solutions se trouve dans le titre de mon commentaire….
    tu récupère les premiers champs , puis les derniers.
    Et le nom de la rue … c'est le reste :)

    Ou alors tu recup les premiers champs, puis tu compte le nombre d'espace que tu as dans le nom de ta rue, et tu enleve les deux derniers champs que tu met dans la ville et le CP.

    ou alors …

    Ensuite, je vais faire mon chieur (mais c'est le but de poser des questions : progresser :) ), mais les constantes dans les programmes sur des longueurs de chaine , ou des décisions arbitraires c'est
    -> pas beau (es-tu sur de ne jamais avoir de fichier mal formé ? de champ ou de ligne dépassant ?)
    -> pas cohérent (tu prend une ligne de 256 octets, et tu cherche 5 champs qui peuvent faire chacun 256 octets…)
    -> redondant et pas claire.
    -> pas optimum. (imagine que tu dois traiter les adresses des 7 milliards d'êtres humains (ou tout chiffre volontairement important) Faut il mieux consomment 1.25Ko/adresse (>8Go au total) ou moins de 256 octets en moyenne (<1.6Go au total)) ? .

    Si tu pense qu'il est "safe" de hardcoder ces limites, tu les mets avec un

    #define MAXOCTETLINE 256
    #define MAXOCTETCHAMP 256

    (et j'ai envie de dire idem avec le nom de fichier …)

    Comme ça, si tu as besoin d'en changer tu ne le fais qu'une fois et tu ne risques pas d'en oublier quelque part…
    En plus c'est quand meme plus parlant de voir MAXOCTETCHAMP que 256…

    Mais il est bien plus propre de ne pas faire de suppositions sur ces limites, et de les attribuer de façon dynamique.
    Le peu d'optimisation que tu aurais pu avoir avec des longueur hardcodé, tu l'a perdu car tu fais un malloc.

    Ensuite, aucune vérification d'erreur :'(
    tu fais quoi si un malloc te renvoie null?

    Pour résumer :
    -> faire soit même l'algorithme de sélection des champs plutôt que de laisser fscanf tout faire
    -> Commencer, dès les premiers programmes, à bien gérer les erreurs . C'est une habitude qui se prend, et crois moi, ça évite de nombreux casse tête

    -> Commencer dans un second temps, à prendre les bonnes longueurs pour les données. Ni trop, ni pas assez. Ca permet de trouver plus facilement des erreurs de boundary check (electric fence, valgrind, etc…), limite la conso mémoire, ou simplement que le programme marche sur toutes les données, et pas perdre du temps à comprendre "pourquoi j'ai une adresse tronquée ?"
    Même chose : c'est un mode de fonctionnement qui se prend dès le début. Tu crois que ça te fait coder plus lentement, mais vu le temps que tu peux gagner en débug après…
    Avoir des longueurs hardcodé peut être tout à fait viable si c'est un choix assumé, mais assez rarement pour des données fournies par l'utilisateur sans contrôle… et encore plus rarement lorsque cet utilisateur est un prof ;)

    PS : Question subsidiaire. Que faire si le champ ville contient aussi des espaces? Est-ce jouable ?

    • [^] # Re: backward....

      Posté par  . Évalué à 1.

      Ok merci briaeros007. Par ailleurs comment pourrais je le traduire sous forme de code pour etre plus concret ? Si tu pourrais me donner un exemple ça m'aiderait.
      Merci d'avance

  • # Commentaire supprimé

    Posté par  . Évalué à 3. Dernière modification le 05 décembre 2015 à 10:23.

    Ce commentaire a été supprimé par l’équipe de modération.

Suivre le flux des commentaires

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