Fichier CSV : Colonnes qui disparaissent

Eléphanteau du PHP | 14 Messages

18 juin 2015, 15:11

Bonjour à tous,

Je travaille avec deux fichiers A.csv et B.csv suivants :

Fichier A :

Code : Tout sélectionner

Login;Nom;Prenom;Note;Bis ia123;Turquier;Simon;50;50 if456;Frot;Anthony;20;99 ic789;Lagalis;Roman;99;13
Fichier B :

Code : Tout sélectionner

Login;Nom;Prenom;Note;Bis ia123;Turquier;Simon;50;50 if456;Frot;Anthony;2;05 ic789;Lagalis;Roman;99;11
Je dois comparer les deux fichiers, modifier la valeur du login A selon le login B
ainsi que définir la meilleure note dans le fichier A :
par exemple si la note dans A.csv est de 02/20 et dans B.csv 18/20
on écrase la valeur de A.
A aura donc une note de 18/20

A l'inverse si A = 15/20 et B = 04/20
A garde la note de 15/20

Cependant lorsque je lance le script le contenu est supprimé :

Code : Tout sélectionner

Login;;Prenom;Note;Bis 208123;;;; 213456;;;; 210789;;;;
La suppression parait "aléatoire" :
La colonne "Nom" est entierement supprimée
Les valeurs des colonnes "Prenom", "Note" et "Bis" aussi.

Voici mon code :

Code : Tout sélectionner

$modifId = array( 'ia'=>208, 'ib'=>209, 'ic'=>210, 'id'=>211, 'ie'=>212, 'if'=>213, 'ig'=>214, 'ih'=>215, 'ii'=>216, 'ij'=>217, 'ik'=>218, 'il'=>219, 'im'=>220, 'in'=>221, 'io'=>222, 'ip'=>223, 'iq'=>224, 'ir'=>225, 'is'=>226, 'it'=>227, 'iu'=>228, 'iv'=>229, 'iw'=>230, 'ix'=>231, 'iy'=>232, 'iz'=>233); //Modification de login $openB=fopen("fichierB.csv", "r"); $openA=fopen("fichierA.csv", "w"); while (($data = fgetcsv($openB, 1000, ";")) !== FALSE) { // Impossible de substr un code si c'est le header de la colonne if($data[0] !== "Login") { $codeId = substr($data[0], 0, 2);//On extrait les lettres de la colonne 1 $data[0] = str_replace($codeId, "", $data[0]);//codeId = lettres $codeId = $modifId[$codeId];//Lettres transformées en nombres } //On crée un array composé du code transformé + data $login = array($codeId . $data[0]); fputcsv($openA, $login, ";", "'"); } fclose($openA); fclose($openB); //Comparaison des notes A et B $openB = fopen("fichierB.csv", "r"); while (($data = fgetcsv($openB, 1000, ";")) !== FALSE) { $notesA[$data[0]] = $data; } fclose($openB); $openA = fopen("fichierA.csv", "r"); while (($data = fgetcsv($openA, 1000, ";")) !== FALSE) { // Tant que loginA correspond loginB et nomA correspond nomB $line[0] = $data[0]; $line[1] = $data[1]; for ($i = 2; $i < 5; $i++) { $line[$i] = max($data[$i], $notesA[$data[0]][$i]); } $output[] = $line; } fclose($openA); $openA = fopen("fichierA.csv", "w"); foreach ($output as $line) { // on réécrit dans A avec les nouvelles valeurs fputcsv($openA, $line, ";"); } fclose($openA);

Avatar du membre
Mammouth du PHP | 1609 Messages

18 juin 2015, 15:59

Salut Ghirahim, si je comprends bien ta demande les deux fichiers de tests ne sont pas pertinent car il n'y a aucune modification à apporter sur le fichier A d'après les données du fichiers B. Les login ne changent pas et la note et toujours plus élevée dans A que dans B. D'ailleurs tu parles de note /20 mais on a des valeurs de 50 et 99... pas très clair tout ça.

Ensuite ton code parait bien compliqué pour faire ce qu'il est sensé faire. Quel est l'intérêt du tableau de $modifId qui travaille sur le login (alors que le login peut être amené à changer) ? la valeur de référence pour trouver les lignes qui correspondent entre les deux fichiers ne devrait-elle pas être constituée du nom et du prénom (qui eux ne sont pas sensés changer) ? pour le reste ce n'est pas très lisible, c'est trop compliqué, il y a trop de variables.

Je préconise une réécriture complète du code plutôt que le debug de ce dernier.

Tu devrais plutôt dans un premier temps créer une fonction pour charger les données dans 2 tableaux sous une forme qui te permettra ensuite en bouclant sur le tableau A de comparer simplement le login et la note avec le tableau B et de les mettre à jour dans A le cas échéant.

Ensuite tu pourras terminer par une simple procédure pour re-convertir le tableau A dans le format du fichier CSV.
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

18 juin 2015, 16:13

Salut Saian, deja merci de m'aider.

J'ai oublié de préciser le contexte :
Ces deux fichiers sont juste des representations simplistes des fichiers que je dois traiter :

Je travaille avec le logiciel Apogée, qui me fournit des notes étudiants sur 5 domaines de compétences.
Considérons le fichier Apogée comme A.csv
A me fournit un numero étudiant (par exemple 214 123) , un nom, prénom, et les différentes notes sur un bareme de 20

Je dois comparer ce fichier a un B.csv composé d'un login ( exemple IG 123), nom, prenom, et les différentes notes sur 100.

Les logins et numeros etudiants des A.csv et B.csv correspondent (IG devient 214) grace a l'array liste dégeulasse créé au tout début.

Je dois écraser le fichier A.csv en ne gardant que la meilleure note d'un des deux fichiers.

Aussi, je n'ai pas encore traité la conversion d'un barème sur 100 en barème sur 20, c'est pour cela qu'il y a des notes étranges

Avatar du membre
Mammouth du PHP | 1609 Messages

18 juin 2015, 16:18

Pourrais tu fournir des jeux d'essais réels des 2 fichiers ? pas avec toutes les lignes bien sur mais avec au moins 2, 3 lignes des 2 fichiers concernant les mêmes élèves.
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

18 juin 2015, 16:22

Je n'ai malheureusement pas encore testé sur les fichiers réels. J'ai préféré faire des essais simplistes sur des fichiers courts.

Mais Apogée ressemble a ça :

Code : Tout sélectionner

Login;Nom;Pr_nom;Note;Note;Note;Note;Note 21204251;ABBASSI;ZACHARIE;10,6;9,2;12,5;10;13,6 21204812;ABDALLA;SEEF;18,1;14,2;15,3;12,5;13,6 21204464;ABDELMOUMEN;RAPHER;17,2;17,4;14,5;10,3;15,3 21309863;ABERGEL;YOHANAN;12,5;11,5;11,4;11,1;7,8 21405208;ABITTAN;DAVID ALEXANDER;11;10,8;6,7;10,3;13,3 21205608;ABOU ALI;MOHAMED;15;13,2;13,1;14,2;12 21205644;ABOU JAOUDE;MATHIEU;14,5;15;15;12;15,3 21409604;AHDJOUDJ;MASSI;ABI;ABI;ABI;ABI;ABI 21400841;AIT OUAHMANE;LAMIA;13,1;15,8;12,2;10,6;10
et le fichier B :

Code : Tout sélectionner

login;nom;prenom;D1;D2;D3;D4;D5 ie04251;Abbassi;Zacharie;52.8 %;45.8 %;62.5 %;50.0 %;68.1 % ie04812;Abdalla;Seef;90.3 %;70.8 %;76.4 %;62.5 %;68.1 % ie04464;Abdelmoumen;Rapher;86.2 %;86.8 %;72.3 %;51.4 %;76.4 % if09863;Abergel;Yohanan;62.6 %;57.7 %;56.9 %;55.6 %;38.9 % ig05208;Abittan;David Alexander;54.8 %;54.2 %;33.3 %;51.3 %;66.7 % ie05608;Abou Ali;Mohamed;75.0 %;66.0 %;65.3 %;70.8 %;59.8 % ie05644;Abou Jaoude;Mathieu;72.3 %;75.0 %;75.0 %;59.8 %;76.4 % ig00841;Ait Ouahmane;Lamia;65.3 %;79.2 %;61.2 %;52.8 %;50.0 % ie06935;Ait Sidhoum;Abdelhakim;77.8 %;79.2 %;73.7 %;75.1 %;72.3 %

Avatar du membre
Mammouth du PHP | 1609 Messages

18 juin 2015, 17:01

Généralement le but n'est pas de donner la réponse toute faite mais en l’occurrence il me paraissait plus simple d'écrire un code fonctionnel que d'essayer de t'aiguiller.

Il y a certainement d'autres façons de procéder, peut être plus simple, mais la c'est surtout la préparation des données pour comparaison qui est un peu lourde. Une fois les données préparées, la procédure de mise à jour du tableau est simple.

PS : il reste peut être à ajouter un arrondi à une décimale pour les notes /100 ramenées /20.
/* tableau de convertion de login */
$login_converter = array();
$chars = 'abcdefghijklmnopqrstuvwxyz';
for($i = 0; $i < strlen($chars); $i++)
	$login_converter['i'.$chars[$i]] = 208 + $i;

/* fonction de préparation des données pour comparaisons */
function prepare_datas($datas, $type)
{
	global $login_converter;
	$_datas = array();
	
	foreach(explode("\n", $datas) as $line)
	{
		/* saute la première ligne du fichier */
		if(strtolower(substr($line, 0, 5)) == 'login')
			continue ;
	  
	  /* extrais les colonnes de la ligne */
		list($login, $nom, $prenom, $note_1, $note_2, $note_3, $note_4, $note_5) = explode(';', $line);
		
		/* stocke les données sous une forme exploitable pour les comparaisons */
		/* indexe les données sur nom-prenom pour un accès simple lors des comparaisons */
		$key = strtoupper($nom).'-'.strtoupper($prenom);
		$_datas[$key] = array(
			'login'  => $type == 'other' ? $login_converter[substr($login, 0, 2)].substr($login, 2) : $login,
			'nom'    => $type == 'other' ? strtoupper($nom) : $nom,
			'prenom' => $type == 'other' ? strtoupper($prenom) : $prenom
		);
		
		/* stocke les notes en remplaçant les , par des . dans le cas d'apogée (pour comparaison numérique) et en les mettant /20 dans l'autre cas */
		for($i = 1; $i < 6; $i++) {
			$varname = 'note_'.$i;
			$note = $$varname;
			$_datas[$key][$varname] = $type == 'other' ? substr($note, 0, -2) / 5 : str_replace(',', '.', $note);
		}
	}
	
	return $_datas;
}

/* charge les fichiers */
$a = prepare_datas(file_get_contents('A.csv'), 'apogee');
$b = prepare_datas(file_get_contents('B.csv'), 'other');

/* compare les données et met à jour $a en fonction des comparaisons faites avec $b */
foreach($a as $key => $datas)
{
	/* si l'élève n'existe pas dans B on saute la ligne */
	if(!isset($b[$key]))
		continue ;
	
	/* met à jour le login le cas échéant */
	if($b[$key]['login'] != $datas['login'])
		$a[$key]['login'] = $b[$key]['login'];
		
  /* met à jour les notes le cas échéant */
	for($i = 1; $i < 6; $i++) {
		if($b[$key]['note_'.$i] > $datas['note_'.$i])
			$a[$key]['note_'.$i] = $b[$key]['note_'.$i];
			
		/* remet une , à la place du . */
		$a[$key]['note_'.$i] = str_replace('.', ',', $a[$key]['note_'.$i]);
	}
}

/* enregistre les données à jour */
$csv = array('Login;Nom;Pr_nom;Note;Note;Note;Note;Note');
foreach($a as $key => $datas)
	$csv[] = implode(';', $datas);

file_put_contents('C.csv', implode("\n", $csv));
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

18 juin 2015, 21:24

Woh, tu viens de remettre en cause tout ce que j'ai produis, tu le sais ca ? :P

Je vais étudier à fond ton code, il y a pleins de notions qui sont extrêmement interessantes.
Je t'en redirai d'avantage demain matin.

En tout cas, je te remercie enormement pour le temps que tu as pris. J'ai le droit de t'appeler sensei ?

Avatar du membre
Mammouth du PHP | 1609 Messages

18 juin 2015, 23:28

Merci Ghirahim, ton enthousiasme fait plaisir. :)

PS : j'ai réécris un peu le code pour enlever l'inutile et le rendre plus lisible.
/* tableau de convertion des logins */
$login_converter = array();
$chars = 'abcdefghijklmnopqrstuvwxyz';
for($i = 0; $i < strlen($chars); $i++)
	$login_converter['i'.$chars[$i]] = 208 + $i;

/* fonction de préparation des données pour comparaisons */
function prepare_datas($datas, $type)
{
	global $login_converter;
	$students = array();
	
	foreach(explode("\n", $datas) as $student)
	{
		/* saute la première ligne du fichier */
		if(strtolower(substr($student, 0, 5)) == 'login')
			continue ;
	  
	  /* extrais les colonnes de la ligne */
		list($login, $nom, $prenom, $note_1, $note_2, $note_3, $note_4, $note_5) = explode(';', $student);
		
		/* stocke les données sous une forme exploitable pour les comparaisons */
		/* indexe les données sur nom-prenom pour un accès simple lors des comparaisons */
		$student_id = strtoupper($nom).'-'.strtoupper($prenom);
		$students[$student_id] = array(
			'login'  => $type == 'other' ? $login_converter[substr($login, 0, 2)].substr($login, 2) : $login,
			'nom'    => $nom,
			'prenom' => $prenom
		);
		
		/* stocke les notes en remplaçant les , par des . dans le cas d'apogée (pour comparaison numérique) et en les mettant /20 dans l'autre cas */
		for($i = 1; $i < 6; $i++) {
			$varname = 'note_'.$i;
			$note = $$varname;
			$students[$student_id][$varname] = $type == 'other' ? substr($note, 0, -2) / 5 : str_replace(',', '.', $note);
		}
	}
	
	return $students;
}

/* charge les fichiers */
$students_a = prepare_datas(file_get_contents('A.csv'), 'apogee');
$students_b = prepare_datas(file_get_contents('B.csv'), 'other');

/* compare les données et met à jour $a en fonction des comparaisons faites avec $b */
foreach($students_a as $student_id => $student_a)
{
	/* si l'élève n'existe pas dans B on saute la ligne */
	if(!isset($students_b[$student_id]))
		continue ;
	
	$student_b = $students_b[$student_id];
	
	/* mets à jour le login le cas échéant */
	if($student_b['login'] != $student_a['login'])
		$student_a['login'] = $student_b['login'];
		
  /* mets à jour les notes le cas échéant */
	for($i = 1; $i < 6; $i++) {
		if($student_b['note_'.$i] > $student_a['note_'.$i])
			$student_a['note_'.$i] = $student_b['note_'.$i];
			
		/* remet une , à la place du . */
		$student_a['note_'.$i] = str_replace('.', ',', $student_a['note_'.$i]);
	}
	
	/* met à jour l'étudiant dans le tableau d'origine */
	$students_a[$student_id] = $student_a;
}

/* enregistre les données à jour */
$students = array('Login;Nom;Pr_nom;Note;Note;Note;Note;Note');
foreach($students_a as $student)
	$students[] = implode(';', $student);

file_put_contents('C.csv', implode("\n", $students));
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

19 juin 2015, 10:28

Rebonjour Saian, encore un grand merci de prendre de ton temps pour m'aider, j'ai l'impression que tu fais ça avec tellement d'aisance :lol:
Mais j'ai quelques questions à propos de ton code :oops:

Code : Tout sélectionner

/* fonction de préparation des données pour comparaisons */ function prepare_datas($datas, $type) { global $login_converter; $students = array();
Une variable locale est une variable stockée dans une fonction puis détruite une fois la fonction terminée
A l'inverse, une variable "globale" est réutilisable et donc modifiable ?
Cela correspond il à une forme d'héritage en language objet où une classe est réutilisable et modifiable ?


Par la suite :

Code : Tout sélectionner

/* stocke les données sous une forme exploitable pour les comparaisons */ /* indexe les données sur nom-prenom pour un accès simple lors des comparaisons */ $student_id = strtoupper($nom).'-'.strtoupper($prenom); $students[$student_id] = array( 'login' => $type == 'other' ? $login_converter[substr($login, 0, 2)].substr($login, 2) : $login, 'nom' => $nom, 'prenom' => $prenom );
Tu utilises strtolower/upper pour la notion de casse entre les deux fichiers, c'est ingénieux de ta part j'y avais à peine pensé :mrgreen:
Mais je ne comprend pas ce que tu considères comme "type = autre" au niveau du login


Aussi,

Code : Tout sélectionner

/* stocke les notes en remplaçant les , par des . dans le cas d'apogée (pour comparaison numérique) et en les mettant /20 dans l'autre cas */ for($i = 1; $i < 6; $i++) { $varname = 'note_'.$i; $note = $$varname; $students[$student_id][$varname] = $type == 'other' ? substr($note, 0, -2) / 5 : str_replace(',', '.', $note);
Tu utilises le double dollar pour produire une variable dynamique. Le nom de la variable $varname est lui même une variable, c'est bien cela ?


Encore un grand merci pour ce que tu as écris

Avatar du membre
Mammouth du PHP | 1609 Messages

19 juin 2015, 14:40

Rebonjour Ghirahim, ça fait une bonne quinzaine d'années que je fais du php ce qui explique que j'ai une certaine facilité dans l'écriture du code. Ceci dit, même après toutes ces années, je ne maîtrise pas toutes les finesses du langage.

Pour le premier point c'est une question de portée des variables. Pour résumer la portée d'une variable dépend du contexte dans lequel elle est déclarée. Et en effet, une variable déclarée dans une fonction à une portée limitée à cette fonction. Elle n'est pas accessible à l'extérieur de la fonction et elle n'écrasera pas la valeur d'une variable de même nom dans un contexte extérieur à la fonction. De même dans le contexte d'une fonction on n'a pas accès aux variables déclarées en dehors de cette fonction.

J'ai déclaré $login_converter en dehors de la fonction afin quelle ne soit pas systématiquement ré-déclarée à chaque appel de la fonction. Mais du coup comme elle est déclarée dans le contexte principal (ou global), pour pouvoir y accéder dans la fonction, il faut faire appel au mot clé global afin d'indiquer que l'on souhaite utiliser la variable déclarée dans le contexte global.
La variable aurait pu être passée à la fonction en paramètre mais dans le cas présent j'ai préféré utiliser le mot clé global dans la fonction plutôt que devoir ajouter le paramètre à chaque appel de la fonction.

Sinon $login_converter n'est jamais modifié dans la fonction mais effectivement si on la modifiait elle serait modifiée dans le contexte global car il s'agit bien de la variable du contexte global.

http://php.net/manual/fr/language.variables.scope.php

Le strtolower/upper ça reste un grand classique pour trouver une correspondance de chaîne dans le cas où les casses peuvent être différentes. T'y avais pas pensé mais ça deviendra automatique à force.

Pour ce qui est de la variable $type, tu remarqueras qu'il s'agit du deuxième paramètre de la fonction prepare_datas. Il permet dans la fonction de faire la différence entre le CSV provenant d'apogée et l'autre CSV. Si tu regardes bien les 2 appels à prepare_datas, dans le premier cas $type prend la valeur 'apogee' et dans le deuxième cas la valeur 'other'.
/* charge les fichiers */
$students_a = prepare_datas(file_get_contents('A.csv'), 'apogee');
$students_b = prepare_datas(file_get_contents('B.csv'), 'other');
Cela permet dans la fonction d'appliquer les traitements adéquats aux valeurs selon qu'elles proviennent d'un fichier ou de l'autre. Par exemple dans le cas du fichier qui ne vient pas d'apogée ($type == 'other') il faut retraiter le login pour remplacer les lettres par la valeur numérique correspondante. Dans l'autre cas il n'y a pas de transformation à appliquer.

Et pour ce code :
for($i = 1; $i < 6; $i++) {
      $varname = 'note_'.$i;
      $note = $$varname;
Il s'agit en effet de faire appel au variables $note_1, $note_2, etc de façon dynamique plutôt que d'écrire 5 fois la même ligne de code avec juste la variable de note qui change.
Au fil de la boucle $varname prend les valeurs note_1, note_2, etc et ainsi $$varname revient à faire appel aux variables $note_1, $note_2, etc.

http://php.net/manual/fr/language.varia ... riable.php
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

22 juin 2015, 10:10

Rebonjour Saiain !
Merci pour tes explications j'ai bien compris la structure et les enchainements de ton script

Il ne me reste plus désormais qu'à effectuer un simple

Code : Tout sélectionner

fputcsv(C.csv, $students, ";", '"');
puisque le "file_put_contents" effectue les fonctions "fopen" et "fgetcsv" ?

Avatar du membre
Mammouth du PHP | 1609 Messages

22 juin 2015, 12:20

Salut, pas vraiment. Selon la doc file_put_contents "Revient à appeler les fonctions fopen(), fwrite() et fclose() successivement."

fputcsv sert à ajouter UNE ligne dans le fichier (que tu auras préalablement ouvert avec fopen), or dans $students tu as tous les étudiants qui représentent donc plusieurs lignes.

Et tu ne peux pas écrire fputcsv(C.csv,... qui de toute façon fera une erreur sur C.csv.

L'utilisation du fputcsv ressemblerait plutôt à ceci :
$fp = fopen('C.csv', 'w');
foreach ($students as $student) {
    fputcsv($fp, $student, ';', '"');
}
fclose($fp);
Sauf que ça, ça serait à condition que chaque ligne de $students soit un tableau or dans mon code j'ai fait une boucle avec :
$students[] = implode(';', $student);
Ou chaque ligne de $students est donc une chaine de caractères où les colonnes de $student sont concaténées par un ;

Il faudrait donc à la place de ce code :
$students = array('Login;Nom;Pr_nom;Note;Note;Note;Note;Note');
foreach($students_a as $student)
  $students[] = implode(';', $student);

file_put_contents('C.csv', implode("\n", $students));
Mettre celui ci :
$fp = fopen('C.csv', 'w');
fputcsv($fp, array('Login', 'Nom', 'Pr_nom', 'Note', 'Note', 'Note', 'Note', 'Note'), ';', '"');
foreach ($students_a as $student) {
    fputcsv($fp, $student, ';', '"');
}
fclose($fp);
A noter que dans ce cas le formatage du fichier de sortie n'est donc plus le même que celui du fichier d'entrée ou il n'y a pas de délimiteur ".

PS : je dirai que le premier code est un peu plus sauvage, tandis que le deuxième est un peu plus académique. Et le deuxième sera plus adapté s'il y a beaucoup de lignes à traiter (quand je dis beaucoup, je pense à des milliers voir même plutôt des dizaines de milliers de lignes).
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

23 juin 2015, 22:55

Bonsoir (oui c'est encore moi .-.)
j'ai effectué un fputcsv cependant cela retourne dans le fichier C.csv seulement" l'entête" crée :

Code : Tout sélectionner

/* enregistre les données à jour */ $students = array('Login;Nom;Pr_nom;Note;Note;Note;Note;Note');
Il n'y a pas de tableau ni de valeur;

Aussi j'ai effectué des print_R et il semblerait que les différents tableaux students / students_a / students_b soient vides
Je ne comprend pas :?

Avatar du membre
Mammouth du PHP | 1609 Messages

24 juin 2015, 10:17

Salut Ghirahim, si les tableaux sont vides juste après les 2 appels à prepare_datas c'est probablement que les retours à la ligne des fichiers ne sont pas des \n et le foreach ne se ferait donc qu'une fois avec tout le contenu du fichier :
foreach(explode("\n", $datas) as $student)
Remplace le \n par un \r et ça devrait aller mieux.
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 14 Messages

24 juin 2015, 10:39

Ca marche déja beaucoup mieux merci :D
Le fichier C est rempli, c'était bien le délimiteur qui posait problème

Le fait que le print_R m'affichait des array vides m'inquiétait, j'avais l'impression que les données n'était pas chargées ou prises en compte.

Aussi je me demandais : je viens de constater qu'il y a aussi des étudiants présents dans le fichier B et absents du A
C'est "l'inverse" du cas précédent.

Dois je donc reprendre ton code pour gérer ce cas de figure, ou existe-t-il des fonctions ou méthodes pour gérer cette "exception" ? Je vais créer un fichier D.csv
D'une certaine manière c'est simplement une ligne X présent dans un fichier B et absent dans un fichier A