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 :

lire une cellule de fichier CSV avec fgetcsv si la cellule du CSV est écrite sur plusieurs lignes


Sujet :

Langage PHP

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

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

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 374
    Points : 5 734
    Points
    5 734
    Billets dans le blog
    1
    Par défaut lire une cellule de fichier CSV avec fgetcsv si la cellule du CSV est écrite sur plusieurs lignes
    Bonsoir,

    j'ai un code pour lire des fichiers CSV qui fonctionne bien si les cellules sont écrites sur une seule ligne mais qui échoue si elles sont écrites sur plusieurs lignes. Peut-on me dire ce qu'il faudrait faire ?

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     $enclosure   = empty(self::$enclosure) ? chr(8) : self::$enclosure;
     while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure)) !== false) {
          $key = self::$header;
          $value = $row;
     
          self::$data[] = array_combine($key, $value);
    }
    La ligne qui définit $enclosure n'est pas de moi mais je vois que $enclosure vaut \b.

    Si une cellule du fichier CSV est écrite sur plusieurs lignes, j'ai ce message d'erreur :
    Fatal error: Uncaught ValueError: array_combine(): Argument #1 ($keys) and argument #2 ($values) must have the same number of elements in C:\projets\ticket_rawsrc\model\CSVParser.php:119 Stack trace: #0 C:\projets\ticket_rawsrc\model\CSVParser.php(119): array_combine(Array, Array) #1 C:\projets\ticket_rawsrc\model\CSVImport.php(38): ticket_rawsrc\model\CSVParser::parse('C:\\projets\\tick...', 4096) #2 C:\projets\ticket_rawsrc\controller\CSVImportController.php(72): ticket_rawsrc\model\CSVImport::uploadFile('C:\\projets\\tick...') #3 C:\projets\ticket_rawsrc\index.php(112): ticket_rawsrc\controller\CSVImportController->invoke(Array) #4 {main} thrown in C:\projets\ticket_rawsrc\model\CSVParser.php on line 119
    et il semblerait que ça vient du fait que seule la première ligne de la cellule du fichier CSV est prise en compte (c'est ma déduction, mais n'en suis pas sûr)

    Si je fais du debug :
    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
    21
    22
    23
    24
    25
    26
    27
    $enclosure   = empty(self::$enclosure) ? chr(8) : self::$enclosure;
    while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure)) !== false) {
          $key = self::$header;
          $value = $row;
          UtilFct::debug("csvparser 99 row ",$row);
          //deb debug
          echo '<br/><br/>key count : ', count($key).'<br/>';
          echo 'value count : ', count($value)."<br/><br/>";
          $i=0;
          foreach($key as $one_key)
          {
              echo "key(".$i++.")";
              var_export($one_key);
               echo '<br/>';
          }
          $i=0;
     
          foreach($value as $one_value)
          {
                echo "value(".$i++.")";
                var_export($one_value);
                echo '<br/>';
           }
           echo "<br/><br/>";
           //end debug
           self::$data[] = array_combine($key, $value);
    }
    ça donne :
    csvparser 99 row
    array(5) {
    [0]=>
    string(7) "sept-21"
    [1]=>
    string(3) "MEA"
    [2]=>
    string(2) "No"
    [3]=>
    string(3) "Web"
    [4]=>
    string(24) ""ISSUE: Opus - OE Tools\"
    }



    key count : 6
    value count : 5

    key(0)'mois de months'
    key(1)'regions (1)'
    key(2)'reopened'
    key(3)'reported source'
    key(4)'resolution'
    key(5)'resolution ola status'
    value(0)'sept-21'
    value(1)'MEA'
    value(2)'No'
    value(3)'Web'
    value(4)'"ISSUE: Opus - OE Tools\\'
    alors que la cellule sur plusieurs lignes contient :
    ISSUE: Opus - OE Tools
    CAUSE: I cannot install new OPUS Tool  issue is I do not have Administrator 
    RESOLUTION: 
    DID USER CONFIRMED ISSUE RESOLVED: Yes
    To obtain more detailed information about this ticket, you just have to go to the 2929IT Portal Ho
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  2. #2
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 091
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 091
    Points : 8 194
    Points
    8 194
    Billets dans le blog
    17
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $enclosure   = empty(self::$enclosure) ? chr(8) : self::$enclosure;
    chr(8) est le backspace, bizarre de l'utiliser en tant que délimiteur
    Pour du CSV $enclosure devrait plutôt valoir " par défaut

    fgetcsv() supporte bien les champs multi-lignes. Il faut par contre que ces champs soient correctement délimités par $enclosure.

    Ton CSV est-il valide en ce sens ?
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

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

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

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 374
    Points : 5 734
    Points
    5 734
    Billets dans le blog
    1
    Par défaut
    Merci pour ta réponse. J'étais pas dispo pour répondre plus tôt

    Je viens d'essayer : $enclosure = empty(self::$enclosure) ? chr(22) : self::$enclosure;.

    Mon CSV :
    Mois de Months;Regions (1);Reopened;Reported Source;Resolution;Resolution OLA Status
    sept-21;MEA;No;Phone;Please retrigger the profile.;Missed
    sept-21;MEA;No;Web;"ISSUE: Opus - OE Tools
    CAUSE: I cannot install new OPUS Tool  issue is I do not have Administrator 
    RESOLUTION: 
    DID USER CONFIRMED ISSUE RESOLVED: Yes
    To obtain more detailed information about this ticket, you just have to go to the 2929IT Portal Ho";Missed
    
    Même message d'erreur...
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  4. #4
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 091
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 091
    Points : 8 194
    Points
    8 194
    Billets dans le blog
    17
    Par défaut
    Pourquoi utiliser chr() pour exprimer un littéral imprimable ? chr() rend le script difficile à lire et bugogène => 22 n'est pas 0x22 Met directement ton délimiteur

    Et puis changer la valeur par défaut ne change pas l'éventuel mauvais self::$enclosure
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

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

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

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 374
    Points : 5 734
    Points
    5 734
    Billets dans le blog
    1
    Par défaut
    Cette fois, je répond un peu plus vite
    Si j'ai mis chr(22), c'est que j'avais d'abord essayé $enclosure = empty(self::$enclosure) ? " : self::$enclosure; mais que comme un peu plus loin, j'initialise des chaînes de caractères (donc avec des "), il est perdu. Comment faut faire ?

    Citation Envoyé par Séb. Voir le message
    Et puis changer la valeur par défaut ne change pas l'éventuel mauvais self::$enclosure
    C'est le seul endroit où on initialise cette propriété donc self::$enclosure a peu de risque d'être mauvais, non ?
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  6. #6
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 091
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 091
    Points : 8 194
    Points
    8 194
    Billets dans le blog
    17
    Par défaut
    Si j'ai mis chr(22), c'est que j'avais d'abord essayé $enclosure = empty(self::$enclosure) ? " : self::$enclosure; mais que comme un peu plus loin, j'initialise des chaînes de caractères (donc avec des "), il est perdu
    Je ne comprends pas, et ta syntaxe n'est pas valide.

    C'est le seul endroit où on initialise cette propriété donc self::$enclosure a peu de risque d'être mauvais, non ?
    Je ne vois pas d'initialisation de self::$enclosure dans ton script.

    Tu ne montres pas comment tu utilises ton code, et je répète chr(8) et chr(22) ne représentent pas un " => Mets un " directement
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

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

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

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 374
    Points : 5 734
    Points
    5 734
    Billets dans le blog
    1
    Par défaut
    Je ne comprends pas en quoi mon code n'est pas valide. Peux-tu préciser ?

    Le code complet de ma fonction est :
    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
    public static function parse(string $filepath, int $buffer = 4096): bool
    {
    if (( ! is_file($filepath)) || (false === (self::$handle = fopen($filepath, 'r')))) {
    throw new Exception("impossible to open the file '$filepath'");
    }
    if (self::detectSyntaxFromHeader($filepath) && self::detectCSVType()) {
    $enclosure   = empty(self::$enclosure) ? " : self::$enclosure;
    while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure)) !== false) {
    $key = self::$header;
    $value = $row;
     
    self::$data[] = array_combine($key, $value);
    }
    return true;
    }
    return false;
    }
    J'ai enlevé le debug, donc les affichages, mais le message d'erreur est le même :
    Parse error: syntax error, unexpected token "]", expecting "-" or identifier or variable or number in C:\projets\ticket_rawsrc\model\CSVParser.php on line 120
    La ligne 120 étant ici la 12.

    Sinon, exact, on initialise pas self::$enclosure ici.

    C'est fait par la méthode detectSyntaxFromHeader :
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     // séparateur
            if (mb_stripos($row, ',') !== false) {
                self::$separator = ',';
            } elseif (mb_stripos($row, ';') !== false) {
                self::$separator = ';';
            } else {
                self::$errors[] = 'Unable to detect the CSV field separator';
                return false;
            }
     
            $char = mb_substr($row, 0, 1); // premier caractère
            if ($char === '\xef\xbb\xbf') {  //if first character is BOM, one begins with the next character
                $row  = mb_substr($row, 1);
                $char = mb_substr($row, 0, 1);
            }
     
            if (($char === '"') && (mb_stripos($row, '"'.self::$separator.'"') !== false)) {
                self::$enclosure = '"';
            } elseif (($char === "'") && (mb_stripos($row, "'".self::$separator."'") !== false)) {
                self::$enclosure = "'";
            }
     
            if (self::$enclosure) {
                // on vérifie que le premier séparateur trouvé est bien entouré de lettres ASCII
                $i = mb_stripos($row, self::$separator);
                if ($i) {
                    $fc = mb_substr($row, $i - 1, 1);
                    $lc = mb_substr($row, $i + 1, 1);
                    if (ctype_alpha($fc) && ctype_alpha($lc)) {
                        self::$enclosure = '';
                    }
                }
            }
            else  self::$enclosure = '';
    Que voulais-tu dire ?
    Citation Envoyé par Séb. Voir le message
    Et puis changer la valeur par défaut ne change pas l'éventuel mauvais self::$enclosure
    Citation Envoyé par Séb. Voir le message
    Tu ne montres pas comment tu utilises ton code
    L'objet de la méthode est la construction de la propriété self::$data[]. Je vois pas ce que tu souhaites ?
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  8. #8
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 091
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 091
    Points : 8 194
    Points
    8 194
    Billets dans le blog
    17
    Par défaut
    Je ne comprends pas en quoi mon code n'est pas valide. Peux-tu préciser ?
    Ici tu ne délimites pas ta chaîne, d'où l'erreur PHP :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $enclosure   = empty(self::$enclosure) ? " : self::$enclosure;
    Si tu veux affecter " il faut le délimiter avec des ' en faisant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $enclosure   = empty(self::$enclosure) ? '"' : self::$enclosure;

    Pour ta culture, tu peux aussi faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $enclosure   = empty(self::$enclosure) ? "\"" : self::$enclosure;
    Ou avec la notation décimale du code caractère :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $enclosure   = empty(self::$enclosure) ? chr(34) : self::$enclosure;
    Ou avec la notation hexadécimale :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $enclosure   = empty(self::$enclosure) ? chr(0x22) : self::$enclosure; // Et non pas chr(22) comme plus haut
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

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

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

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 374
    Points : 5 734
    Points
    5 734
    Billets dans le blog
    1
    Par défaut
    En effet, du coup, le code passe et $enclosure vaut bien ". Par contre, c'est du type string et c'est ça qui m'inquiétait et m'avait empêché d'encadrer le caractère.

    Cela dit, mon besoin a disparu vu que étant allé un peu plus loin, je m'étais posé une autre question et Willy_k m'a proposé une solution sans $enclosure : https://www.developpez.net/forums/d2...d-fichier-csv/
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  10. #10
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 091
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 091
    Points : 8 194
    Points
    8 194
    Billets dans le blog
    17
    Par défaut
    Citation Envoyé par laurentSc Voir le message
    En effet, du coup, le code passe et $enclosure vaut bien ". Par contre, c'est du type string et c'est ça qui m'inquiétait et m'avait empêché d'encadrer le caractère.
    Pas de soucis, c'est bien ce qu'attend fgetcsv() dont le prototype est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    fgetcsv(
        resource $stream,
        int $length = 0,
        string $separator = ",",
        string $enclosure = '"',
        string $escape = "\\"
    ): array
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

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

Discussions similaires

  1. Lire une liste de fichier mp3 avec un raspberry
    Par frederic13870 dans le forum Shell et commandes GNU
    Réponses: 10
    Dernier message: 20/06/2019, 12h46
  2. Impossible de lire une cellule dans un fichier fermé
    Par Mic13710 dans le forum Macros et VBA Excel
    Réponses: 6
    Dernier message: 09/06/2015, 22h57
  3. [Débutant] Lire une cellule dans un fichier excel
    Par Annihil dans le forum C#
    Réponses: 1
    Dernier message: 15/11/2011, 07h32
  4. Lire une cellule dans un autre fichier
    Par jurgh dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 08/09/2009, 17h22
  5. lire une cellule dans une DBGrid
    Par flo74 dans le forum Bases de données
    Réponses: 10
    Dernier message: 30/01/2006, 08h52

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