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

Langage PHP Discussion :

array_map(str_getcsv..) m'a piégé


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut array_map(str_getcsv..) m'a piégé
    Bonjour,

    mon code lit des fichiers csv et le but est d'en extraire chaque valeur. Or le comportement de mon code diffère selon le délimiteur utilisé dans le fichier csv.

    Mon code :
    Code php : 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
     $data = array_map('str_getcsv', file(DIR_ROOT.$upload_dir.DIRECTORY_SEPARATOR.$name)); 
        /* file(xxx) returns an array where each cell is a line of the file passed into parameter.
        array_map executes str_getcsv on each cell of the array returned by file.
        each cell of the array returned by file is a line of the csv file ; str_getcsv puts each elt of the csv file in a cell of an array.*/
     
     
        if (isset($data)) { //only if array $data exists
            //one detects the delimitator
            $Nb_Comas=substr_count($data[0][0], ',');
            $Nb_Semicolons=substr_count($data[0][0], ';');
            if ($Nb_Semicolons > $Nb_Comas) $sep=';'; else $sep=',';
     
            $nb_lines_arr=sizeof($data);
            $val="";
            for ($j=1;$j<$nb_lines_arr;$j++) {  // for each line of the array
                $data[$j]=implode($data[$j]); //if several cells in a line, all is concatenated into a unique line
                $val_split=explode($sep,$data[$j]);//creation of the array $val_split (each cell is a data of the CSV file)
                var_dump($val_split);                   
            }
        }

    Le csv de test initial avait ; comme séparateur et tout allait bien. Ensuite, j'ai testé avec le même csv sauf que le séparateur était une virgule et patatras.
    str_getcsv selon la doc analyse une chaîne de caractères représentant des champs au format CSV et retourne un tableau contenant tous les champs lus.
    C'est bien le cas si le délimiteur est un ; mais pas si c'est ,

    var_dump de la ligne 18
    avec ;
    array (size=23)
    0 => string 'SESA100008' (length=10)
    1 => string '(none)' (length=6)
    2 => string 'Software Engineering' (length=20)
    3 => string 'SESA69723' (length=9)
    4 => string 'CollabNet' (length=9)
    5 => string 'TeamForge_Full' (length=14)
    6 => string '' (length=0)
    7 => string 'TRUE' (length=4)
    8 => string 'Jul 26 2018' (length=11)
    9 => string '' (length=0)
    10 => string '' (length=0)
    11 => string 'France' (length=6)
    12 => string 'Bilhel' (length=6)
    13 => string '---' (length=12)
    14 => string 'Schneider Electric France' (length=25)
    15 => string 'TYS5' (length=4)
    16 => string '' (length=0)
    17 => string 'Industry Business' (length=17)
    18 => string 'CARROS HORIZON' (length=14)
    19 => string '---@schneider-electric.com' (length=42)
    20 => string 'Marc' (length=4)
    21 => string '---' (length=6)
    22 => string '---@schneider-electric.com' (length=34)
    avec ,
    array (size=1)
    0 => string 'SESA100008(none)Software EngineeringSESA69723CollabNetTeamForge_FullTRUEJul 26 2018FranceBilhel---Schneider Electric FranceTYS5Industry BusinessCARROS HORIZON---@schneider-electric.com---...r-electric.com' (length=253)
    Comment faire ? Merci d'avance.

  2. #2
    Membre Expert
    Avatar de badaze
    Homme Profil pro
    Chef de projets info
    Inscrit en
    Septembre 2002
    Messages
    1 412
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets info
    Secteur : Transports

    Informations forums :
    Inscription : Septembre 2002
    Messages : 1 412
    Par défaut
    Si tu lis la doc de la fonction str_getcsv tu verras que le séparateur par défaut est la virgule et non pas le point-virgule.

    Si tu avais mis un var_dump($data) tout de suite après l'array_map tu aurais compris. Compris que ton texte avec une virgule fait que $data est un array et que le même texte avec un point-virgule fait que $data est une chaine de caractère et que c'est le reste du code qui fait tout le boulot comme si str_getcsv n'avait servi à rien. Au final pas de piège. Une dernière chose. Ton code ne fonctionne pas s'il n'y a qu'une ligne dans ton fichier.


    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    function str_getcsv_v($text) {
     return str_getcsv($text);
     
    }function str_getcsv_pv($text) {
     return str_getcsv($text,';');
    }
     
    $data = array_map('str_getcsv_pv', file("ton fichier.csv")); 
     
    var_dump($data);

  3. #3
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut
    Merci de me répondre si tard. En fait, après avoir constaté le problème, j'ai mis un var_dump et ai compris la différence, mais n'ai pas encore réussi à faire marcher mon code dans le cas de la virgule. Ca serait quand même plus simple s'il était possible de passer un argument à la fonction str_getcsv quand je la passe à array_map...

  4. #4
    Membre Expert
    Avatar de badaze
    Homme Profil pro
    Chef de projets info
    Inscrit en
    Septembre 2002
    Messages
    1 412
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets info
    Secteur : Transports

    Informations forums :
    Inscription : Septembre 2002
    Messages : 1 412
    Par défaut
    Je ne comprends pas ce que tu veux faire.

  5. #5
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut
    désolé, je ne suis en mesure de répondre que maintenant !

    Voilà l'idée : je reçois des fichiers csv (dont le séparateur est parfois la virgule et parfois le point-virgule) dont le contenu est une liste de tickets relatants des problèmes rencontrés sur des outils et dont certaines informations doivent être mémorisées en base de données pour interrogation ultérieure (nom de l'outil, date de soumission, status, id du submitter, etc).
    Donc pour faire ça, je réalise un outil (codé en PHP et exécutable avec Wampserver) qui devra lire ces fichiers, et alimenter une bdd MySQL (création ou mise à jour).
    Je suis conscient de pas être précis mais pour pas donner des infos inutiles, merci de me poser des questions.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    montre des exemples significatifs de fichiers CSV que tu veux traiter.

  7. #7
    Modératrice
    Avatar de Celira
    Femme Profil pro
    Développeuse PHP/Java
    Inscrit en
    Avril 2007
    Messages
    8 633
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeuse PHP/Java
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2007
    Messages : 8 633
    Par défaut
    Citation Envoyé par laurentSc Voir le message
    Ca serait quand même plus simple s'il était possible de passer un argument à la fonction str_getcsv quand je la passe à array_map...
    Il y a un contournement pour ça : array_map peut travailler avec plusieurs tableaux, pourvu qu'ils fassent la même taille.
    Donc tu pourrais faire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $lines = file(DIR_ROOT.$upload_dir.DIRECTORY_SEPARATOR.$name);
    $seps = array_fill(0, count($lines), ";"); // on créé un tableau de point-virgules de la même taille que le tableau à traiter
    $data = array_map('str_getcsv', $lines, $seps);
    Autre possibilité : créer une fonction personnalisée qui encapsule str_getcsv avec le séparateur souhaité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    function str_get_csv_pv($str) {
        return str_getcsv($str, ";");
    }
    et utiliser la bonne fonction en fonction du besoin.

    La question c'est : as-tu un moyen de savoir dans quel cas de fichier tu te trouves ?
    Modératrice PHP
    Aucun navigateur ne propose d'extension boule-de-cristal : postez votre code et vos messages d'erreurs. (Rappel : "ça ne marche pas" n'est pas un message d'erreur)
    Cherchez un peu avant poser votre question : Cours et Tutoriels PHP - FAQ PHP - PDO une soupe et au lit !.

    Affichez votre code en couleurs : [CODE=php][/CODE] (bouton # de l'éditeur) et [C=php][/C]

  8. #8
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    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 986
    Par défaut
    "El delimitator pasa a Patatras"

    Citation Envoyé par laurentSc
    Dans mon exemple, je ne mets que la ligne d'entête, puis le premier enregistrement.

    fichier csv avec séparateur ,
    Code csv : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SESAID,ALTERNATEIDS,PLATFORM,PLATFORMOWNER,PUBLISHER,APPLICATIONNAME,HFMCODE,CURRENTLYACTIVE,ACTIVATEDATE,DEACTIVATEDATE,GROUPNAME,OFFICECOUNTRY,FIRSTNAME,LASTNAME,COMPANYNAME,JOBCODE,REPORTINGENTITY,BUSINESSUNITNAME,LOCATION,INTERNETADDRESS,MANAGERFIRSTNAME,MANAGERLASTNAME,MANAGEREMAIL
    ...
    fichier csv avec séparateur ;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SESAID;ALTERNATEIDS;PLATFORM;PLATFORMOWNER;PUBLISHER;APPLICATIONNAME;HFMCODE;CURRENTLYACTIVE;ACTIVATEDATE;DEACTIVATEDATE;GROUPNAME;OFFICECOUNTRY;FIRSTNAME;LASTNAME;COMPANYNAME;JOBCODE;REPORTINGENTITY;BUSINESSUNITNAME;LOCATION;INTERNETADDRESS;MANAGERFIRSTNAME;MANAGERLASTNAME;MANAGEREMAIL
    ...
    Si tes fichiers ont un header et que tes noms de champs ont toujours cette tête là (des caractères alphanumériques, pas de caractères spéciaux comme une virgule ou un point virgule), pourquoi ne pas déduire le séparateur de ce header?
    D'autre part, plutôt que de charger ton fichier avec file() qui crée un tableau pour ensuite recréer un autre tableau avec array_map() et pour au final faire une boucle sur chaque ligne, autant lire directement le fichier ligne par ligne en utilisant fgetcsv(), (c'est peut-être moins concis, mais ça te permettra de faire le test du séparateur, et d'économiser de la mémoire).

    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
    if ( false === $handle = fopen(DIR_ROOT.$upload_dir.DIRECTORY_SEPARATOR.$name, 'r') )
        throw new Exception("impossible d'ouvrir le fichier '$name'");
     
    // on récupère le header 
    $buffer = 4096;
     
    if ( false === $header = fgets($handle, $buffer) )
        throw new Exception("Le fichier '$name' est vide");
     
    // on teste différents séparateurs (par ordre de préférence)
    $sep = ','; // par défaut
    $separators = [ ',', ';', '|',  /*etc.*/ ];
     
    foreach ($separators as $separator) {
        if ( strpos($header, $separator) ) {
            $sep = $separator;
            break;
        }
    }
     
    // On peut ensuite boucler sur les lignes et récupérer les champs
    while ( false !== $fields = fgetcsv($handle, $buffer, $sep) ) {
        // les traitements ici
    }
     
    fclose($handle);

  9. #9
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut
    La solution proposée par CosmoKnacki me plaît vu qu'il y a moyen une fois le délimiteur déterminé d'en tenir compte.
    Difficulté supplémentaire (que je viens de découvrir) : petite différence du contenu des colonnes entre les 2 formats : les 2 types de fichier contiennent la même date, mais dans un format différent

    délimiteur ;
    colonne ACTIVATEDATE : Mmm DD , YYYY (le mois est en lettres (en français) et le reste en chiffres)
    colonne DEACTIVATEDATE : vide

    délimiteur ,
    colonne ACTIVATEDATE : Mmm DD
    colonne DEACTIVATEDATE : YYYY

    Voici le code qui me permet de la mettre au format SQL dans le premier cas :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $date1 = \DateTime::createFromFormat('M d, Y', $date);
    return( $date1->format('Y-m-d H:i:s'));
    Ici, la date est dans une seule cellule du fichier csv. Par contre, dans le 2e cas, la date est répartie sur 2 cellules. Du coup, le code actuel retourne 2019-MM-YY hh:mm:ss au lieu de YY-MM-YY hh:mm:ss. Comment puis-je l'adapter ? Je sèche.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [JavaScript] [SRC] array_map() modification de tous les elements d'un array avec une fonction
    Par SpaceFrog dans le forum Contribuez
    Réponses: 0
    Dernier message: 08/07/2009, 13h28
  2. Réponses: 4
    Dernier message: 20/06/2008, 12h56
  3. Array_map en java?
    Par sunp dans le forum Débuter avec Java
    Réponses: 3
    Dernier message: 30/04/2008, 20h24
  4. Fonction str_getcsv() inconnue
    Par pc.bertineau dans le forum Langage
    Réponses: 2
    Dernier message: 21/06/2007, 11h13
  5. [Tableaux] Soucis avec array_map
    Par dunbar dans le forum Langage
    Réponses: 18
    Dernier message: 02/09/2006, 08h23

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