Forum |  HardWare.fr | News | Articles | PC | Prix | S'identifier | S'inscrire | Aide | Shop Recherche
391 connectés 

  FORUM HardWare.fr
  Programmation
  PHP

  PHP Trier un fichier csv volumineux

 



 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

PHP Trier un fichier csv volumineux

n°2333043
gigidenant​es
Lurkeur compulsif
Posté le 06-05-2019 à 14:37:35  profilanswer
 

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.

mood
Publicité
Posté le 06-05-2019 à 14:37:35  profilanswer
 

n°2333044
flo850
moi je
Posté le 06-05-2019 à 14:48:56  profilanswer
 

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

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

n°2333051
gigidenant​es
Lurkeur compulsif
Posté le 06-05-2019 à 15:53:25  profilanswer
 

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

n°2333083
rufo
Pas me confondre avec Lycos!
Posté le 06-05-2019 à 21:27:16  profilanswer
 

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 Cantine Calandreta : http://sourceforge.net/projects/canteen-calandreta
n°2333416
B4X
kebab-case
Posté le 12-05-2019 à 13:24:30  profilanswer
 

[:pikitfleur:1]  
 
[:ca m'gaaave:2]  
 
Il faut clairement déléguer ça à shell.
 
Sous Linux, qqchose comme:

Code :
  1. system("sort -t';' -k3 mon_fichier.csv > fichier_tri_date.csv 2>&1" );


 
J'utilise volontiers ce genre de syntaxe pour les traitements coûteux [:cosmoschtroumpf]
Ca me libère immédiatement php et ça tourne en tâche de fond dans un pid indépendant [:cosmoschtroumpf]
 
Quand je suis motivé j'output en + dans un fichier txt, comme ça je peux même faire une progress bar pour l'utilisateur  [:saad fraug:5] typiquement pour lancer un encodage video sous ffmpeg après un upload, deplacer des gros repertoires, mysqldump, wget, etc
 
1 ligne de code au lieu de 60, je sais pas ce que t'en pense [:cosmoschtroumpf]


---------------
In vanitas veritas.
n°2333429
rufo
Pas me confondre avec Lycos!
Posté le 12-05-2019 à 20:55:08  profilanswer
 

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 Cantine Calandreta : http://sourceforge.net/projects/canteen-calandreta
n°2337020
jSynergy
Posté le 22-07-2019 à 07:21:40  profilanswer
 

+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é).


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


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  PHP

  PHP Trier un fichier csv volumineux

 

Sujets relatifs
ontroler la présence d'un fichier par son extension et sortir en codeAfficher le contenu d'un fichier log dans une page HTML
Code fichier php est-il accessible depuis un navigateur ? Protection ?Trier un fichier trace
Envoyer un message après le send d'une fichier via socketPassage de paramètre du PHP au Javascript
Recherche script PHP pour créer des bases de donnéesTrace du mail envoyé avec PHP
Décalage dans un fichier txt 
Plus de sujets relatifs à : PHP Trier un fichier csv volumineux


Copyright © 1997-2018 Hardware.fr SARL (Signaler un contenu illicite) / Groupe LDLC / Shop HFR