Comment lire un fichier csv avec séparateur ; pour structure

[Fermé]
Signaler
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015
-
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015
-
Bonjour,

Je souhaiterais savoir comment lire un fichier csv avec séparateur ";".

Car je voudrais renseigné une structure avec les informations lus dans un fichier.

ex :

nom;prenom;date_de_naissance;lieu_de_naissance;

Mais aussi prendre en compte les champs vide afin de ne pas perturber ma structure

ex :

nom;prenom;;lieu_de_naissance;


Mais aussi comment récupérer ces infos et renvoyer ces valeurs dans m structure ?

Merci pour votre aide.

4 réponses

Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015

Ci dessous l'ébauche du code. (je ne voudrais pas qu'on pense que j'attend que quelqu'un fasse mon travail.)
Par contre j'ai vraiment besoin d'un coup de main.

Merci d'avance.

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

struct Adresse
{
char Societe[40];
int ClientSite;
int ClientCompta;
char Groupe[50];
char Fjuri[54];
char Nom[45];
char Enseigne[45];
char Adresse1[45];
char Adresse2[45];
int Cpost;
char Ville[45];
int Tel;
int Fax;
char Email[40];
int Siret;

};

int nbLignesAdresseClients(void);
char *dest;
char *fichier;


struct Adresse client[nbLignesAdresseClients] ;
Int nbClient ;

Char *membresAdresse[14] ;


char *fichierAdresseClient="Adresse clients.csv"; // NOUVEAU
char *copieFichierAdresseClient="Donnees/Adresse Clients.txt"; // ANCIEN


int main()
{
lireFichierAdresse;
montrer();
ecrireFichierAdresse;

return(0);
}

int compte(FILE *fichierLignes) /* compteur de lignes */
{
int c;
int nLignes = 0;
int c2 = '\0';

while((c=fgetc(fichierLignes)) != EOF)
{
if(c=='\n')
nLignes++;
c2 = c;
}

/* Ici, c2 est égal au caractère juste avant le EOF. */

if(c2 != '\n')nLignes++; /* Dernière ligne non finie */

return nLignes;
}

int nbLignesAdresseClients(void) /*Compteur lignes fichier Adresse Clients */
{
FILE *fichierLignes = fopen(fichierAdresseClient, "r");
if(fichierAdresse != NULL)
{
int nLignes = compte(fichierLignes);

printf("Nombre de lignes : %d\n", nLignes);

fclose(fichierLignes);
}
else
puts("Erreur en ouverture du fichier : Adresses Clients.");
return 0;
}


void lireFichierAdresse(void)
{
FILE *f;
struct Adresse charge;
f = fopen(fichierAdresseClients, "r");
if(!f) /* Fichier introuvable !*/
return;
/* Lecture totale */
while(fread(&client, sizeof(client), 1, f))
{
void lectureChampsAdresse (f);

/* Lecture des valeurs du fichier, */
client.Societe=charge.Societe;
client.ClientSite=charge.ClientSite;
client.ClientCompta=charge.ClientCompta;
client.Groupe=charge.Groupe;
client.Fjuri=charge.Fjuri;
client.Nom=charge.Nom;
client.Enseigne=charge.Enseigne;
client.Adresse1=charge.Adresse1;
client.Adresse2=charge.Adresse2;
client.Cpost=charge.Cpost;
client.Ville=charge.Ville;
client.Tel=charge.Tel;
client.Fax=charge.Fax;
client.Email=charge.Email;
client.Siret=charge.Siret;

/* pas des pointeurs ! */


}
fclose(f);
}

void ecrireFichierAdresse(void)
{
FILE *f;
if(client == NULL) /* Liste vide */
{
puts("Rien a sauvegarder !");
return;
}

f = fopen(copieFichierAdresseClient, "w");
if(!f)
{
puts("Erreur en ouverture de fichier");
exit(1);
}
while(client) /* Dernier enreg == NULL */
{
fwrite(&client, sizeof(client), 1, f);
}
fclose(f);
} ;

/* Affiche tous les enregistrements de la liste */
void montrer(void)
{
if(client == NULL) /* Liste vide */
{
puts("Rien a afficher");
return;
}
puts("Affichage complet :");
while(client) /* Dernier == NULL */
{
printf("%s\n",client.Societe);
printf("%d\n",client.ClientSite);
printf("%d\n",client.ClientCompta);
printf("%s\n",client.Groupe);
printf("%s\n",client.Fjuri);
printf("%s\n",client.Nom);
printf("%s\n",client.Enseigne);
printf("%s\n",client.Adresse1);
printf("%s\n",client.Adresse2);
printf("%s\n",client.Cpost);
printf("%s\n",client.Ville);
printf("%d\n",client.Tel);
printf("%d\n",client.Fax);
printf("%s\n",client.Email);
printf("%d\n",client.Siret);
}
} ;

void lectureChampsAdresse (char* dest, File* fichierLu) //lecture d'un champ
{
int i=0
int a=0
Adresse charge;
Char site [20] = {0};
Char comptable [20] = {0};
Char cp [5] = {0};

while (a < 14)
{
c=fgetc(fichierLu) ; // on lit le caractere
if (c !=EOF && c!= `;')dest[i]=c; //si le caractere est valide
i++; // on passe au caractere suivant
membresAdresse[a] = dest //on enregistre la valeur dans un tableau de char
a++ ;

}

Site = membresAdresse[1] ;
Comptable = membresAdresse[2];
Cp = membresAdresse[9];


if (site, 20)
{
// Si lecture du texte ok, convertir le nombre en long et
le retourner
return strtol(site, NULL, 10);
}
else
{
// Si problème de lecture, renvoyer 0
return 0;
}

membresAdresse[1] = site ;

if (comptable, 20)
{
// Si lecture du texte ok, convertir le nombre en long et
le retourner
return strtol(comptable, NULL, 10);
}
else
{
// Si problème de lecture, renvoyer 0
return 0;
}


membresAdresse[2] = comptable ;

if (cp, 20)
{
// Si lecture du texte ok, convertir le nombre en long et
le retourner
return strtol(cp, NULL, 10);
}
else
{
// Si problème de lecture, renvoyer 0
return 0;
}

membresAdresse[9] = cp ;


charge.Societe = membresAdresse[0];
charge.ClientSite = membresAdresse[1];
charge.ClientCompta = membresAdresse[2];
charge.Groupe = membresAdresse[3];
charge.Fjuri = membresAdresse[4];
charge.Nom = membresAdresse[5];
charge.Enseigne = membresAdresse[6];
charge.Adresse1 = membresAdresse[7];
charge.Adresse2 = membresAdresse[8];
charge.Cpost = membresAdresse[9];
charge.Ville = membresAdresse[10];
charge.Tel = membresAdresse[11];
charge.Fax = membresAdresse[12];
charge.Email = membresAdresse[13];
charge.Siret = membresAdresse[14];



While (c!=EOF && c != ` ;') ; //fin de lecture
Dest [i] = `\0'; //fin de chaine
}

Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021
945
Je n'ai pas regardé en détails ton code, mais j'ai tenté de le compiler.

Tu as écrit un code de 261 lignes qui ne compile pas dès la 30ème ligne et qui renvoie 65 lignes d'erreurs et avertissements.

Ce n'est pas comme cela qu'on crée un programme. Dès que tu as écris quelques lignes, compile et teste. Dès que tu as écris quoi que ce soit de fonctionnel, compile, teste et vérifies en le fonctionnement. Tu n'attends d'avoir écrit pas loin de 300 lignes, pour ne pas savoir quoi en faire et le balancer sur un forum.

Ton code, qui ne compile pas, n'illustre pas ta question car il n'est pas limité à ta question (en fait les très nombreuses erreurs montrent que tu devrais te poser plein d'autres questions et les résoudre avant d'en arriver à celle que tu poses).

Ta question est, en résumé, comment faire en C pour séparer les champs d'une ligne de texte séparés par des ;

Une réponse est : tu peux te servir de strtok pour le faire : http://www.cplusplus.com/reference/cstring/strtok/

Pour lire des lignes de texte dans un fichier texte, j'utiliserais fgets, pas fread.

Je ne comprends pas ce que tu veux tester en écrivant des tests tels que
if (site, 20)
, tu as des ` au lieu de ' pour délimiter un caractère, tu appelles une fonction
lireFichierAdresse()
dans main en omettant d'utiliser des parenthèses, tu mets des majuscules aux types, tu crées une fonction
nbLignesAdresseClients()
mais tu ne t'en sert pas dans ton code mais dans une déclaration de tableau (de nouveau sans parenthèses, mais que tu les mettes ou pas ce n'est pas la bonne façon de procéder), etc., etc.

Si tu dois stocker les éléments dans une structure, tu as besoin d'un tableau de structures, ou d'une liste chaînée de structures, vu que tu as plus d'une ligne à charger en mémoire. Si tu veux faire un tableau, comme tu sembles vouloir le faire, soit tu fais un tableau de taille statique avec une taille maximale, soit tu déclares un pointeur sur la structure et tu alloues avec malloc suffisamment de mémoire pour le nombre de structures à stocker.

Tu peux alors te servir de
nbLignesAdresseClients()
pour déterminer au préalable la mémoire dynamique nécessaire à partir du nombre de lignes (et donc de structures à stocker).

Dal
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015

Bonjour,
N y aurait il personne pour me donner un petit coup de pouce ?

Merci.
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015

Bonjour,

J'ai essayer de simplifier le programme, afin 'y aller étape par étape, mai s je ne comprends pas ou cela bogue.

Lorsque je le lance la consol s'ouvre et un message apparait me signalant que Windows n'a pas fonctionner.

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // pour strchr()
#define TAILLE_MAX 1000

int recupererInfos(char *chaine, int longueur, FILE *fichier);
char *fgets( char *str, int num, FILE *stream );
char **str_split (char *s, const char *ct);
void ficlire(void);


int main(int argc, char *argv[])
{
lireFichier();

return 0;
}

int recupererInfos(char *chaine, int longueur, FILE *fichier)
{
char *positionEntree = NULL;

// On lit le texte du fichier
if (fgets(chaine, longueur, fichier) != NULL) // Pas d'erreur de saisie ?

{
positionEntree = strchr(chaine, '\n'); // On recherche l'"Entrée"

if (positionEntree != NULL) // Si on a trouvé le retour à la ligne
{
*positionEntree = '\0'; // On remplace ce caractère par \0
}
else
{
viderBuffer();
}
return 1; // On renvoie 1 si la fonction s'est déroulée sans erreur
}
else
{
viderBuffer();
return 0; // On renvoie 0 s'il y a eu une erreur
}
}

void viderBuffer()
{
int c = 0;
while (c != '\n' && c != EOF)
{
c = getchar();
}
}

char **str_split (char *s, const char *ct)
{
   char **tab = NULL;

   if (s != NULL && ct != NULL)
   {
      int i;
      char *cs = NULL;
      size_t size = 1;

// Tant que strtok nous renvoi une adresse non nulle 
      for (i = 0; (cs = strtok (s, ct)); i++)
      {
         if (size <= i + 1)
         {
            void *tmp = NULL;

// on augmente la taille du tableau d'une case dans laquelle on stocke l'adresse retournée par (A)
            size <<= 1;
            tmp = realloc (tab, sizeof (*tab) * size);
            if (tmp != NULL)
            {
               tab = tmp;
            }
            else
            {
               fprintf (stderr, "Memoire insuffisante\n");
               free (tab);
               tab = NULL;
               exit (EXIT_FAILURE);
            }
         }
/* (A) */
         tab[i] = cs;
         s = NULL;
      }
      tab[i] = NULL;
   }
   return tab;
}

// lit un fichier ligne a ligne
void lireFichier(void)
{
static const char filename[] = "listebenevoles.csv";
FILE *f;
f = fopen(filename, "r");

if ( f != NULL )
{
while ( fgets ( ligne, sizeof ligne, f ) != NULL ) 
{
char *nom;

char *membres;

recupererInfos(nom, TAILLE_MAX, f);

membres = str_split (nom, ';');


printf(" %s",membres);

}
fclose ( f );
}
else
{
perror ( filename ); 
}
return (0);
}
Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021
945
Salut Rhamm94,

1.

En compilant ton programme, on a les warnings et erreurs suivants (avec gcc en compilant avec les warnings) :

$ gcc -Wall rhamm94_new.c
rhamm94_new.c: In function `main':
rhamm94_new.c:14:2: warning: implicit declaration of function `lireFichier' [-Wimplicit-function-declaration]
rhamm94_new.c: In function `recupererInfos':
rhamm94_new.c:35:4: warning: implicit declaration of function `viderBuffer' [-Wimplicit-function-declaration]
rhamm94_new.c: At top level:
rhamm94_new.c:46:6: warning: conflicting types for `viderBuffer' [enabled by default]
rhamm94_new.c:35:4: note: previous implicit declaration of `viderBuffer' was here
rhamm94_new.c:97:6: warning: conflicting types for `lireFichier' [enabled by default]
rhamm94_new.c:14:2: note: previous implicit declaration of `lireFichier' was here
rhamm94_new.c: In function `lireFichier':
rhamm94_new.c:105:19: error: `ligne' undeclared (first use in this function)
rhamm94_new.c:105:19: note: each undeclared identifier is reported only once for each function it appears in
rhamm94_new.c:113:4: warning: passing argument 2 of `str_split' makes pointer from integer without a cast [enabled by default]
rhamm94_new.c:55:8: note: expected `const char *' but argument is of type `int'
rhamm94_new.c:113:12: warning: assignment from incompatible pointer type [enabled by default]
rhamm94_new.c:125:2: warning: `return' with a value, in function returning void [enabled by default]


Avec un compilateur normalement constitué (quel compilateur utilises-tu ?), ce programme ne compile pas, car "ligne" en ligne 105 de ton code n'est pas déclarée.

TAILLE_MAX, est-ce la taille maximale d'une ligne ?

Les warnings peuvent aussi cacher des problèmes et doivent être traités. Tu déclares une fonction ficlire() qui n'existe pas et tu ne déclares pas les fonctions qui existent (cf. warnings "implicit declaration of function"), les protypes de tes déclarations de fonctions doivent correspondre à leur implémentation (cf. warnings "conflicting types for"), si tes fonctions n'ont pas d'arguments, déclares les explicitement avec "void".

Dans un premier temps sert toi des messages du compilateur, en commençant par le premier warning ou erreur. Corrige le problème qui cause le warning ou l'erreur, et recompile. Passe ensuite au nouveau premier warning ou erreur signalé (qui peuvent être différents après la correction), etc... jusqu'à ce que tu arrives à faire fonctionner *une* fonctionnalité de ton programme (retire le code des fonctionnalités suivantes pour y voir clair). Ne passe pas à la fonctionnalité suivante, tant que tu n'as pas fait fonctionner la première fonctionnalité sans erreurs ni warnings à la compilation, et sans erreurs à l'exécution.

Lorsque ton compilateur ne se plaint plus de la forme de ton code, tu y vois plus clair pour t'interroger sur le fond et la logique de ton code.

2.

Quelques indications sur le fond.

Ta fonction recupererInfos() contient du code pour lire une ligne avec fgets (et tu n'as pas besoin de vider le buffer clavier, vu que ce n'est pas une saisie clavier qui est traitée, mais c'est une autre histoire). Alors, que dans ta fonction lireFichier(), qui appelle recupererInfos(), tu utilises déjà fgets pour lire une ligne (à condition que tu déclares ta variable, bien sur). Alors, il faut choisir où tu mets le code qui lit une ligne du fichier texte.

Là, ton code ne te permet pas de lire le fichier ligne par ligne. Essaye déjà de faire cela (sans *aucun* warning ni erreur) :

1- ouvrir le fichier
2- lire une ligne du fichier dans une chaîne de caractères C
3- afficher cette chaîne de caractères C
4- boucler sur 2 tant que le fichier contient des lignes
5- fermer le fichier

Poste ton code lorsque tu auras fait cela. Ensuite, tu pourras t'attaquer au traitement de chaque ligne.

3.

Enfin, dans ton énoncé de départ (c'est un devoir ?) tu indiquais que tu devais renseigner "une structure avec les informations". Là ta structure (
struct
) a disparu.

Si tu fais ce programme sans contraintes particulières, il est important de savoir quel est l'objectif et les fonctionnalités que tu attends de ton programme, et de décider en fonction de cela de la façon dont tu vas l'implémenter.

Par contre, si c'est un devoir et qu'on te demande d'utiliser des structures... ben... il faudra que tu les utilises.. non ?

Mais là on anticipe sur quelque chose qui dépasse la première étape, on verra cela quand tu auras produit un code correspondant à la fonctionnalité décrite en 2.

Dal
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015

Bonjour DAL,

Merci de vous avoir intéressé a mon soucis.
J'utilise Code Blocks et ce dernier se lançait sans me signaler ces erreurs.
Peut être s'agit il d'un problème de paramétrage chez moi.

Je vous rassure, il ne s'agit pas d'un devoir.
J'essaies de mette en place un petit logiciel pour mon association.

Le but est de basculer ces infos dans une structure, je l'ai omis ici afin de simplifier et retrouver plus facilement ou cela bogue.

Je retraite mon programme afin de limiter les erreurs et reviens vers vous.

Merci encore.

Rhamm
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015
>
Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021

Bonjour Dal,

En effet il serait peut être plus sage de faire appel a une base de donnée.
Sqlite me semble être un bon début car je pourrais essayé de le piloter en C.
Je n 'ai plus qu'a trouver un tuto en français.

Le code que vous m'avez fourni ci-dessous est la réponse que je cherchais depuis quelques semaines. Cela va m'être très utile quant à la compréhension du C.
Je débute et ne maîtrise pas suffisamment ce langage.

Merci.

Rhamm
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015
>
Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021

Bonjour,

Apres fait quelques recherche sur des tutoriels sur le SQL, je m'aperçois que cela répond a moitie a mes attentes.
En effet, je souhaiterais n'avoir qu'un exécutable indépendant.
Je voudrais pourvoir créer une application en c et ne me servir que de celui-ci sans être obligé d installer une application de base de données.
Sur sqlite il parle d'une bibliothèque C, ou puis je la trouver (il y en a plusieurs), mais est il indispensable d'installer cette base si par exemple je souhaite installer l'exécutable (via code blocks) sur un autre ordinateur.

Rhamm
Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021
945 >
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015

La bibliothèque SQLite gère des données stockées dans un fichier, et non dans un serveur de bases de données. Tu n'as donc pas de serveur à installer pour traiter la base au moyen de requêtes SQL (contrairement aux SGBD SQL "normaux").

Ton fichier sera créé, alimenté et interrogé au moyen de requêtes SQL passant par un client SQLite. Il y a des clients SQL existants que tu peux utiliser pour créer ta base.

Comme c'est une bibliothèque C, tu peux programmer toi même ton client SQLite en C (ce que tu pourra faire pour l'interroger et l'exploiter).

Le site officiel est sqlite.org

Tu trouves la documentation complète et tout ce dont tu as besoin là :

https://www.sqlite.org/docs.html
https://www.sqlite.org/quickstart.html (avec un programme C implémentant un client SQL simple d'une 30aine de lignes montrant comment ouvrir et fermer une base et envoyer une requête SQL)
https://www.sqlite.org/howtocompile.html
https://www.sqlite.org/cintro.html
https://www.sqlite.org/download.html

Pour "transporter" ton programme d'un ordinateur à un autre, tu auras besoin de ton exécutable, de la DLL SQLite et du fichier contenant ta base de données.

Dal
Messages postés
11
Date d'inscription
lundi 16 mars 2015
Statut
Membre
Dernière intervention
21 octobre 2015
>
Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021

merci Dal.
Messages postés
5585
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
17 septembre 2021
945
en fait, selon la façon dont un champ vide va être représenté en csv, il est possible que strtok ne soit pas adapté (en particulier si un champ vide serait comme ceci ";;", strtok va passer à côté.

voilà un exemple n'utilisant pas strtok et fonctionnant sur un jeu de données avec 4 champs (donc 3 ; séparateurs selon le format standard, il n'y a pas de ; en fin de ligne) comme celui-ci :
nom;prenom;date_de_naissance;lieu_de_naissance
Einstein;Albert;14-03-1879;Ulm (Allemagne)
de Saint-Exupéry;Antoine;29-06-1900;Lyon
Baba;Orhum;;
Chaplin;Charlie;16-04-1889;Walworth (Royaume-Uni)

en procédant comme cela :

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

#define MAX_LINE_LENGTH 256
#define MAX_CSV_RECORDS 1000
                                                                                                                                            
struct a_record {
    char * lastname;
    char * firstname;
    char * birthdate;
    char * place;
};

void split_csv_line(char * line, struct a_record  * rec) {
    char * cur;
    int n = 0;

    /* first field starts at beginning of the line */
    rec->lastname = line;
    /* count ; replace them by '\0' and locate the 
     * other fields */
    cur = line;
    while (cur[0]) {
        if (cur[0] == ';') {
            cur[0] = '\0';
            n++;
            if (n == 1) rec->firstname = cur + 1;
            if (n == 2) rec->birthdate = cur + 1;
            if (n == 3) rec->place = cur + 1;
        }
        cur++;
    }
    if (n != 3) {
        printf("Error: wrong number of fields\n");
        exit(1);
    }
}

int main(void){
    char filename[] = "data.csv";
    struct a_record records[MAX_CSV_RECORDS];
    char * line;
    int rec_count = 0;

    /* get data from file */
    FILE * file = fopen(filename, "r" );
    if (file) {
        line = malloc(MAX_LINE_LENGTH);
        while (fgets (line, MAX_LINE_LENGTH, file)) {
            if (line[strlen(line) - 1] == '\n')
                line[strlen(line) - 1] = '\0';
            split_csv_line(line, &records[rec_count]);
            line = malloc(MAX_LINE_LENGTH);
            rec_count++;
            if (rec_count > MAX_CSV_RECORDS) {
                printf("Error: there are more than %d lines in %s\n",
                        MAX_CSV_RECORDS, filename);
                exit(1);
            }
        }
        fclose(file);
    }
    else {
        perror(filename);
    }

    /* list data */
    int n;
    for (n = 0; n < rec_count; n++)
        printf("l = %s\nf = %s\nb = %s\np = %s\n\n", records[n].lastname, records[n].firstname, records[n].birthdate, records[n].place);

    /* free memory */
    free(line);
    for (n = 0; n < rec_count; n++)
        free(records[n].lastname);

    return 0;
}

donne à l'exécution le résultat suivant :
l = nom
f = prenom
b = date_de_naissance
p = lieu_de_naissance

l = Einstein
f = Albert
b = 14-03-1879
p = Ulm (Allemagne)

l = de Saint-Exupéry
f = Antoine
b = 29-06-1900
p = Lyon

l = Baba
f = Orhum
b =
p =

l = Chaplin
f = Charlie
b = 16-04-1889
p = Walworth (Royaume-Uni)


Dal