PHP Trier un fichier csv volumineux

PHP Trier un fichier csv volumineux - PHP - Programmation

Marsh Posté le 06-05-2019 à 14:37:35    

Bonjour à tous,
 
une question d'algo pour démarrer la semaine.
 
J'ai un fichier csv avec 162 colonnes et 55 000 lignes. Je veux l'ordonner par date (qui est en colonne 3).
 
J'ai d'abord essayé de le mettre dans un tableau et de le trier, mais out of memory...
 
Ce que j'ai réussi à faire ensuite :
1/ Stocker dans un tableau les 3 1ères colonnes
2/ Ordonner ce tableau avec la date (utilisation de usort)
3/ Parcourir le tableau et lire le fichier pour récupérer les autres colonnes correspondantes à la ligne en cours
4/ Ajouter la ligne complète dans un fichier csv en sortie
5/ Remplacer la ligne par une chaîne vide dans le fichier en lecture
 
Voici une partie de mon code :
 

Code :
  1. //First read of the file
  2. while(($data = fgetcsv($handle, 0,';')) !== false)
  3. {
  4.             $tabLigne[$columnNames[0]] = $data[0];
  5.             $tabLigne[$columnNames[1]] = $data[1];
  6.             $tabLigne[$columnNames[2]] = $data[2];
  7.             $dateCreation = DateTime::createFromFormat('d/m/Y', $tabLigne['Date de Création']);
  8.             if($dateCreation !== false)
  9.             {
  10.                 $tableauDossiers[$row] = $tabLigne;
  11.             }
  12.             $row++;
  13.             unset($data);
  14.             unset($tabLigne);
  15. }
  16.         //Order the array by date
  17.         usort(
  18.             $tableauDossiers,
  19.             function($x, $y) {
  20.                 $date1 = DateTime::createFromFormat('d/m/Y', $x['Date de Création']);
  21.                 $date2 = DateTime::createFromFormat('d/m/Y', $y['Date de Création']);
  22.                 return $date1->format('U')> $date2->format('U');
  23.             }
  24.         );
  25.         fclose($handle);
  26.         copy(PATH_CSV.'original_file.csv', PATH_CSV.'copy_of_file.csv');
  27.         for ($row = 3; $row <= count($tableauDossiers); $row++)
  28.         {
  29.             $handle = fopen(PATH_CSV.'copy_of_file.csv', 'c+');
  30.             $tabHandle = file(PATH_CSV.'copy_of_file.csv');
  31.             fgetcsv($handle);
  32.             fgetcsv($handle);
  33.             $rowHandle = 2;
  34.             while(($data = fgetcsv($handle, 0,';')) !== false)
  35.             {
  36.                 if($tableauDossiers[$row]['Caisse Locale Déléguée'] == $data[0]
  37.                         && $tableauDossiers[$row]['Date de Création'] == $data[1]
  38.                         && $tableauDossiers[$row]['Numéro RCT'] == $data[2])
  39.                 {
  40.                     fputcsv($fichierSortieDossier, $data,';');
  41.                     $tabHandle[$rowHandle]=str_replace("\n",'', $tabHandle[$rowHandle]);
  42.                     file_put_contents(PATH_CSV.'copy_of_file.csv', $tabHandle);
  43.                     unset($tabHandle);
  44.                     break;
  45.                 }
  46.                 $rowHandle++;
  47.                 unset($data);
  48.                 unset($tabLigne);
  49.             }
  50.             fclose($handle);
  51.             unset($handle);
  52.         }


 
Le résultat attendu est le bon. Mais le problème c'est le temps passé par ce script (déjà plus d'une heure et il n'en a traité que 1/3).
 
Merci de vos retours.

Reply

Marsh Posté le 06-05-2019 à 14:37:35   

Reply

Marsh Posté le 06-05-2019 à 14:48:56    

tu devrais peut etre le charger dans une base de données

 

Autre possibilité, stocker dans une 4eme colonne le nombre d'octets depuis le début du fichier pour arriver à la bonne ligne (avec fseek)
tu as ftell qui peut te dire ou tu en est dans ton fichier

 

edit : et stocke directement la date formattée dans ton tableau d'index , plutot que de les recalculer à chaque comparaison


Message édité par flo850 le 06-05-2019 à 14:50:29

---------------

Reply

Marsh Posté le 06-05-2019 à 15:53:25    

Merci. Je n'avais pas pensé à fseek et ftell. C'est impeccable.

Reply

Marsh Posté le 06-05-2019 à 21:27:16    

Effectivement, charger le CSV direct dans une table de MySQL, faire le tri puis rediriger le résultat dans un CSV, ça se fait en quelques lignes et ça sera rapide puisque Mysql a des primitives pour manipuler le CSV ;)


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 12-05-2019 à 20:55:08    

Rien ne dit qu'il est sous Linux ;)
 
Edit : et puis la commande system, shell_exec... on l'a pas forcément sur tous les serveurs à cause du safe_mode :o Typiquement, sur du serveur mutualisé, tu ne l'as généralement pas. Passer par une BD ne prend pas beaucoup plus de lignes de code. Comme je l'ai expliqué, MySQL sait importer nativement du CSV. Donc, l'import dans une table, c'est une ligne de requête SQL. Un ORDER By, c'est une 2ème ligne. L'export vers un CSV, c'est une 3ème requête SQL et c'est fini. Cette solution a le mérite d'être cross-OS et non dépendant de la conf safe_mode ou pas du serveur. En plus, cette solution permet de monter beaucoup plus en charge sur des fichiers bien plus gros. Si le fichier fait plusieurs Go, la commande sort de l'OS va galérer alors que pour le SGBD, ça va être tranquille...


Message édité par rufo le 22-07-2019 à 10:08:18

---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
Reply

Marsh Posté le 22-07-2019 à 07:21:40    

+1 pour la solution de rufo, ayant eu à traiter des CSV pesant plusieurs millions de lignes, c'est rapide et robuste (et tu déportes le traitement au SGBD, optimisé pour ça, et ça tourne même si le SGBD se trouve sur un serveur séparé).


---------------

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed