IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PHP & Base de données Discussion :

Importer des datas issues d'un fichier .csv dans une table Postgresql => erreur


Sujet :

PHP & Base de données

  1. #1
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut Importer des datas issues d'un fichier .csv dans une table Postgresql => erreur
    Bonjour,

    J'ai un tableau de données .CSV que je dois importer dans une table.

    Extrait du fichier csv :
    matricule;nom
    1;toto
    2;tata
    3;titi
    J'ai essayé d'utiliser psqlCopyFromArray mais j'ai en retour le message suivant :
    Fatal error: Uncaught PDOException: SQLSTATE[22P04]: Bad copy file format: 7 ERROR: literal newline found in data HINT: Use "\n" to represent newline.
    Voici ma méthode :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    require_once 'connect_bd.php' // connection à la base de données
     
    $filename = $_FILES['userfile']['tmp_name'];  //recuperation du fichier envoyé en POST via le formulaire
     
    //Stockage des données dans un tableau et suppression des éventuelles lignes vides
    $data=[];
     
    $file=fopen($filename,"r");
     
    while (!feof($file)){
        $ligne= fgets($file,1024);
        array_push($data,$ligne);
    }
     
    print_r($data) //=> Array ( [0] => matricule;nom [1] => 1;TOTO [2] => 2;tata [3] => 3;titi ) 
     
     
    $fields=array_shift($data); // Retrieve & remove the field list
    $fields=str_replace(";",",",$fields);
    echo($fields); //=>matricule,nom
     
    print_r($data); //=> Array ( [0] => 1;TOTO [1] => 2;tata [2] => 3;titi ) 
     
    $delimiter = ";";
    $null_as=null;
     
    $result=$pdo->pgsqlCopyFromArray('geolis.r_import',$data,$delimiter,$null_as,$fields);
    Message d'erreur :

    Fatal error: Uncaught PDOException: SQLSTATE[22P04]: Bad copy file format: 7 ERROR: literal newline found in data HINT: Use "\n" to represent newline. CONTEXT: COPY r_import, line 3 in C:\ms4w\Apache\htdocs\cyneclic\fdcm\www\espace_prive\geolis\geolis_query2.php:45 Stack trace: #0 C:\ms4w\Apache\htdocs\cyneclic\fdcm\www\espace_prive\geolis\geolis_query2.php(45): PDO->pgsqlCopyFromArray('geolis.r_import', Array, ';', '', 'matricule,nom\r\n') #1 {main} thrown in C:\ms4w\Apache\htdocs\cyneclic\fdcm\www\espace_prive\geolis\geolis_query2.php on line 45
    Si quelqu'un peut me donner une piste pour résoudre ce problème, je suis preneur.

    Bon courage à tous

  2. #2
    Expert éminent sénior
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 465
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 465
    Points : 15 849
    Points
    15 849
    Par défaut
    il y a peut être des sauts de ligne à la fin de chaque donnée. vous pouvez essayer de les retirer comme cela :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    $data = array_map(function ($e) {return trim($e);}, $data);

  3. #3
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 931
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 931
    Points : 6 769
    Points
    6 769
    Par défaut
    Tu peux essayer de remplacer $ligne = fgets($file, 1024); par $ligne = stream_get_line($file, 1024, "\n");.

    stream_get_line fait exactement la même chose que fgets à la différence qu'il ne renvoie pas le délimiteur qu'on lui indique.

  4. #4
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut
    Bonjour,

    Je viens d'essayer ta méthode et j'ai toujours un message d'erreur :

    Fatal error: Uncaught PDOException: SQLSTATE[22P04]: Bad copy file format: 7 ERROR: literal newline found in data HINT: Use "\n" to represent newline. CONTEXT: COPY r_import_territoire, line 396 in C:\ms4w\Apache\htdocs\cyneclic\fdcm\www\espace_prive\geolis\geolis_query2.php:66 Stack trace: #0 C:\ms4w\Apache\htdocs\cyneclic\fdcm\www\espace_prive\geolis\geolis_query2.php(66): PDO->pgsqlCopyFromArray('geolis.r_import...', Array, ';', '', 'MATRICULE,CHASS...') #1 {main} thrown in C:\ms4w\Apache\htdocs\cyneclic\fdcm\www\espace_prive\geolis\geolis_query2.php on line 66
    Par contre j'ai essayé le code suivant et je n'ai plus d'erreur mais je ne sais pas vraiment pourquoi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    while (($line = fgetcsv($file)) !== FALSE) {
      //$line is an array of the csv elements
       $line = implode(',',$line);
       array_push($data,$line);
    }
    Si vous avez une explication je suis preneur.
    Merci pour votre aide

  5. #5
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut
    Re bonjour,

    Nouveau problème :

    Lors de mon export en csv depuis ma base métier, il y a une ligne vide à la fin :
    Nom : Sans titre.png
Affichages : 90
Taille : 9,4 Ko

    Si je supprime cette ligne manuellement tout fonctionne. Est ce qu'il y a un moyen d'ignorer cette ligne automatiquement ?

    Merci

  6. #6
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 931
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 931
    Points : 6 769
    Points
    6 769
    Par défaut
    Si ça n'a pas marché avec stream_get_line c'est sans doute que la séquence de nouvelle ligne utilisé dans ton fichier n'est pas juste \n mais \r\n, il suffit juste de le préciser.

    Maintenant tu te compliques la vie: puisque tu passes toutes tes données d'un coup à ta base de données, pas la peine de te fatiguer avec fopen et une lecture ligne par ligne. Utilise directement la fonction file qui te mettra chaque ligne dans un tableau et qui en plus par le biais d'une option permet d'enlever automatiquement la séquence de nouvelle ligne et de filtrer les lignes vides (donc la dernière qui te pose problème).


    [EDIT]C'est bien array_shift et pas array_unshift[/EDIT]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    $data = file($filename, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
     
    $fields = strtr(array_shift($data), ';', ','); // doit-on vraiment changer le délimiteur ?
     
    $delimiter = ';';
    $null_as = null;
     
    $result = $pdo->pgsqlCopyFromArray('geolis.r_import', $data, $delimiter, $null_as, $fields);
    Ce type de code fonctionnera sous réserve qu'aucun de tes champs ne soit entre double quotes pour protéger un saut de ligne, sinon utilise fgetcsv.

  7. #7
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut
    Re,

    Ta méthode est effectivement plus cohérente par rapport à la problématique.

    Mais j'ai toujours le même problème, aucune données dans la base de données.
    Pour que l'import fonctionne, il faut que j'ouvre le .csv et que je me positionne après le dernier élément et que je supprime le "vide". C'est incompréhensible.

  8. #8
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut
    Pour info, j'ai ouvert le fichier source .csv (avec Visual Studio Code) et je l'ai enregistré sous un autre nom et cela fonctionne. Ce n'est pas lié à la dernière ligne ou au traitement php mais à quoi cela peut être du ? Des idées ?

  9. #9
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 931
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 931
    Points : 6 769
    Points
    6 769
    Par défaut
    Citation Envoyé par micakel_53 Voir le message
    Pour que l'import fonctionne, il faut que j'ouvre le .csv et que je me positionne après le dernier élément et que je supprime le "vide". C'est incompréhensible.
    Pour déterminer le ou les caractères non visibles à la fin tu peux utiliser ce genre de script:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $URI = 'chemin/vers/le/fichier.csv';
     
    $fp = fopen($URI, 'rb');
     
    fseek($fp, -10, SEEK_END);
     
    while(false !== $c = fgetc($fp)) {
        printf('%02X ', ord($c));
    }
     
    fclose($fp);
    Je ne l'ai pas testé mais il doit afficher sous forme hexadécimale la valeur des 10 derniers octets du fichier.

  10. #10
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut
    Voilà le résultats mais je ne sais pas comment l'interpréter .
    53 3B 35 33 31 32 31 3B 0D 0A

    J'ai aussi fait a même démarche avec le second fichier (idem fichier d'origine avec juste une ouverture et un enregistré sous ) et j'ai la même suite : 53 3B 35 33 31 32 31 3B 0D 0A. Et pourtant le deuxième fichier passe ?!?!?

  11. #11
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 931
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 931
    Points : 6 769
    Points
    6 769
    Par défaut
    Les octets qui s'affichent correspondent simplement à des caractères de la table ASCII:
    Code txt : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    53 3B 35 33 31 32 31 3B 0D 0A
     S  ;  5  3  1  2  1  ; \r \n
    Ce test permet de détecter n'importe quel caractère non visible (un caractère de contrôle, ou un caractère blanc indéterminé, un espace en fin de ligne ou de fichier…).

    J'ai une théorie sur ton fichier de départ. Je pense que les lignes de celui-ci sont séparées par un simple caractère "retour chariot" (0D CR carriage return, qu'on représente dans une chaîne avec \r), c'est la convention pour les anciens OS Apple (jusqu'à Mac OS 9 inclus), puis, et là je ne sais pas par quel mystère, le fichier s'achève sur un "retour chariot" suivi d'une "nouvelle ligne" (0A LF line feed, qu'on représente dans une chaîne avec \n), c'est la convention Windows.

    Autrement dit, ton fichier original doit avoir cette tête là (sous forme de chaîne php):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $text = "field1;field2\r"
          . "Jean-Louis s'est fait;mordre par un ragondin.\r"
          . "Claudette mange de la;choucroute en cachette.\r"
          . "Bogdan joue à Hippo-Glouton;avec son cousin.\r\n";
    J'ai procédé à quelques tests avec cette chaîne et pour les versions de PHP inférieures à 8.1.0.
    Lorsque l'option auto_detect_line_endings est à true, la fonction file (et d'autres fonctions comme fgets) vont se baser sur la première séquence rencontrée pour déterminer quelle est la séquence de nouvelle ligne: donc soit \r (Mac OS ≤ 9), soit \r \n (Windows), soit \n (Unix/Linux/Mac OS > 9). Comme la première séquence rencontrée est \r, il applique cette convention jusqu'à la fin et donc une fonction comme file avec ses options FILE_IGNORE_NEWLINES et FILE_SKIP_EMPTY_LINES renverra une dernière ligne uniquement composée du caractère \n. Du fait de cette détection, \n n'est pas considéré comme une nouvelle ligne (et n'est pas "ignoré", c-à-d enlevé), et la ligne n'est pas considérée comme vide non plus.

    Si mon hypothèse est juste:
    • tu utilises une version de PHP inférieure à 8.1.0.
    • la commande var_dump(ini_get('auto_detect_line_endings')); devrait afficher true.
    • Lorsque tu compares la taille du fichier original avec la taille du fichier enregistré avec VSCode, cette dernière doit être supérieur d'un nombre d'octets correspondant au nombre de lignes moins la dernière (car VSCode remplace tous les \r isolés par \r\n).


    Bref comment se sortir de ce bourbier?

    Dans l'idéal, ce serait de traiter le problème en amont, en comprenant comment ce mélange de saut de lignes s'est produit, en corrigeant de manière à obtenir soit \n partout, soit \r\n partout. (Mieux vaut éviter \r seul qui n'est plus guère utilisé).

    Si tu n'as pas la main sur la création de ces fichiers, il va falloir ruser coté PHP, et ce n'est pas si simple qu'il n'y parait, car le réglage auto_detect_line_endings devient obsolète (et sans effet) à partir de PHP 8.1.0 (pour être totalement supprimé avec PHP 9). Du coup, il n'y a plus de solution pérenne pour détecter le \r isolé comme séquence de nouvelle ligne automatiquement, et par voie de conséquence, plus de solution pérenne utilisant file ou une lecture ligne par ligne. (pour illustrer le problème, essaye avec PHP 8.1.0 ou supérieure).

    Reste à charger le fichier d'un coup avec file_get_contents puis à le splitter:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $content = file_get_contents($filename);
    $data = preg_split('~[\r\n]+~', $content, -1, PREG_SPLIT_NO_EMPTY);
     
    $fields = strtr(array_shift($data), ';', ',');
     
    $delimiter = ';';
    $null_as = null;
     
    $result = $pdo->pgsqlCopyFromArray('geolis.r_import', $data, $delimiter, $null_as, $fields);
    preg_split découpe une chaîne selon le motif fourni en paramètre, ici [\r\n] est une classe de caractères contenant \r et \n et le + est le quantificateur signifiant 1 ou plus. Donc la chaîne est découpée selon toute séquence contenant au moins un de ces deux caractères (ça ne fait pas dans le détail). L'option PREG_SPLIT_NO_EMPTY élimine les morceaux vides résultants de cette opération.
    Bien évidemment, ce n'est pas l'idéal de traiter un fichier csv de cette manière, mais ce n'est pas vraiment un fichier csv.

  12. #12
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2017
    Messages
    41
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Mayenne (Pays de la Loire)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Décembre 2017
    Messages : 41
    Points : 15
    Points
    15
    Par défaut
    Bonjour,

    Déjà je voulais vous dire un grand merci pour votre implication dans la recherche de solutions, CosmoKnacki .

    Pour être franc je n'ai pas encore essayé ta proposition CosmoKnacki, mais je vais testé demain et je te ferai un retour.

    Par contre, au cours de mes multiples essais de code à un moment j'ai eu un message d'erreur sur l'encodage du csv. J'ai donc creusé un peu plus la piste... J'ai modifié l'encodage du fichier et le miracle est apparu !!!!! Mes données sont injectées dans ma table.
    Au passage j'ai modifié l'approche en téléchargeant le fichier sur le serveur. J'ai le sentiments que le traitement est un peu plus rapide (j'ai 142511 lignes avec 17 colonnes dans le fichier csv) mais c'est peut être juste un sentiment.

    Je vous partage le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    //recuperation des infos sur le fichier upload
    $ArrayFile=$_FILES['userfile'];
     
    //stockage des erreurs
    $FileErrors=(['envoie'=>'', 'error_upload'=>'','poids_fichier'=>'','extension'=>'','uploaded'=>'','lien'=>'' ]);
     
     
    if ( $ArrayFile['error']==4){
        $FileErrors['envoie']='Aucun fichier transmis';
    }else if ( $FileErrors['error']!=0){
      $FileErrors['error_upload']= 'Erreur de téléchargement (code '.$ArrayFile['error'].')';
     
    }else{
      $FileErrors['envoie'] ='fichier transmis';
      $FileErrors['error_upload'] = 'Téléchargement correct (code '.$ArrayFile['error'].')'; 
      //test sur le poids du fichier
      if($Arrayfile['size']<=10000000){
      $FileErrors['poids_fichier'] ='Le poids du fichier est accepté (<1MO)';
      //test de l'extension
      $extensions_autorisees=array('csv','CSV');
     
       $extension_upload = pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION);
         if(in_array($extension_upload,$extensions_autorisees)){
          $FileErrors['extension'] ='extension autorise';
          $CheminFile  = 'uploads/data_retrievers.csv';
          if( move_uploaded_file($ArrayFile['tmp_name'],$CheminFile)){
            $FileErrors['uploaded'] ='success';
            $FileErrors['lien']=$CheminFile;}else{
              $FileErrors['uploaded'] ='Erreur';
          }
        }else{
        $FileErrors['extension'] ='extension  non autorise';
        }
      }else{
      $FileErrors['poids_fichier'] ='Le poids du fichier n\'est accepté (>1MO)';
      }
     
     
    }
    echo '<pre>';
    print_r($FileErrors);
    echo '</pre>';
     
    if (!empty($FileErrors['lien'])){
      $fileURL = $FileErrors['lien'];
      $contents = file_get_contents($fileURL);  
      $file = fopen($fileURL,'w+');
      fputs($file, mb_convert_encoding($contents,'UTF-8','ISO-8859-2'));
      fclose($file);
     
     
      $data = file($fileURL, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
      //echo '<pre>'; print_r($data); echo '</pre>';
     
      $fields=array_shift($data);
      $fields=str_replace(";",",",$fields);
      $fields=strtolower($fields);
      $delimiter = ';';
      $null_as = '\\N';
      $result = $pdo->pgsqlCopyFromArray('geolis.r_import_territoire', $data, $delimiter, $null_as, $fields);
     
     
    if ($result==true){ echo 'Importation des données réussies'; }else{ echo 'Echec'; }
     
    }
    Encore MERCI

Discussions similaires

  1. Importer un fichier csv dans une table mdb
    Par pobrouwers dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 02/12/2007, 15h17
  2. [WD9] Importer un fichier csv dans une table
    Par cactus666 dans le forum WinDev
    Réponses: 1
    Dernier message: 01/10/2007, 15h07
  3. Réponses: 3
    Dernier message: 15/05/2007, 10h28
  4. Importer un fichier CSV dans une table mySQL
    Par crazydiver_e2 dans le forum Requêtes
    Réponses: 4
    Dernier message: 16/01/2007, 11h47
  5. Impossible d'importer un fichier csv dans une table sous MySQL
    Par manue85 dans le forum SQL Procédural
    Réponses: 5
    Dernier message: 20/04/2006, 13h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo