web-dev-qa-db-fra.com

Traitement du fichier CSV dans un tableau avec des en-têtes de colonne pour la clé

J'ai un fichier CSV avec la première ligne contenant les noms de champs. Exemple de données est ...

"Make","Model","Note"
"Chevy","1500","loaded"
"Chevy","2500",""
"Chevy","","loaded"

J'ai besoin que mes données soient formatées dans un tableau de paires clé-valeur où le nom de clé est l'en-tête de colonne. Je suppose que ce serait quelque chose comme ça pour la rangée 1:

$array = [
    "Make" => "Chevy",
    "Model" => "1500",
    "Note" => "loaded"
];

... rangée 2 ...

$array = [
    "Make" => "Chevy",
    "Model" => "1500",
    "Note" => ""
];

... et la rangée 3 ...

$array = [
    "Make" => "Chevy",
    "Model" => "",
    "Note" => "loaded"
];

Je ne sais pas comment procéder autrement que statiquement - le problème est que les colonnes avec leurs données associées pourraient changer d'un fichier à l'autre ... colonnes réorganisées, supprimées ou ajoutées.

Vos idées sont très appréciées.

18
Bit Bucket
$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
  $all_rows[] = array_combine($header, $row);
}
print_r($all_rows);
45
Tim Cooper

PHP offre déjà 99,9% de ce dont vous avez besoin dans SplFileObject, vous ajoutez les 0,1% manquants en les prolongeant. Dans l'exemple suivant, CSVFile s'étend à partir de celui-ci:

$csv = new CSVFile('../data/test.csv');

foreach ($csv as $line)
{
    var_dump($line);
}

Avec vos données d'exemple:

array(3) {
  ["Make"]=>  string(5) "Chevy"
  ["Model"]=> string(4) "1500"
  ["Note"]=>  string(6) "loaded"
}
array(3) {
  ["Make"]=>  string(5) "Chevy"
  ["Model"]=> string(4) "2500"
  ["Note"]=> string(0) ""
}
array(3) {
  ["Make"]=>  string(5) "Chevy"
  ["Model"]=> string(0) ""
  ["Note"]=>  string(6) "loaded"
}

CSVFile est défini comme suit:

class CSVFile extends SplFileObject
{
    private $keys;

    public function __construct($file)
    {
        parent::__construct($file);
        $this->setFlags(SplFileObject::READ_CSV);
    }

    public function rewind()
    {
        parent::rewind();
        $this->keys = parent::current();
        parent::next();
    }

    public function current()
    {
        return array_combine($this->keys, parent::current());
    }

    public function getKeys()
    {
        return $this->keys;
    }
}

Si vous le faites de cette façon, les détails sont bien encapsulés. De plus, il est plus facile de traiter les erreurs (par exemple, incompatibilité de comptage) dans la fonction current(), de sorte que le code qui utilise les données ne nécessite pas de traitement.

Modifier:

Cependant, l'exemple donné est court en termes de ré-utilisabilité. Au lieu d'étendre de SplFileObject , il est préférable de l'agréger:

class KeyedArrayIterator extends IteratorIterator
{
    private $keys;

    public function rewind()
    {
        parent::rewind();
        $this->keys = parent::current();
        parent::next();
    }

    public function current()
    {
        return array_combine($this->keys, parent::current());
    }

    public function getKeys()
    {
        return $this->keys;
    }
}

Le code est identique, mais les détails encapsulés dans le constructeur sont omis. Cette réduction permet d’utiliser le type plus largement, par ex. avec (mais pas seulement avec) ledit SplFileObject :

$file = new SplFileObject('../data/test.csv');
$file->setFlags($file::READ_CSV);

$csv = new KeyedArrayIterator($file);

foreach ($csv as $line) {
    var_dump($line);
}

Si cela semble trop bavard, vous pouvez le replier pour lui donner une façade plus agréable:

class CSVFile extends KeyedArrayIterator
{
    /**
     * @param string $file
     */
    public function __construct($file)
    {
        parent::__construct(new SplFileObject($file));
        $this->setFlags(SplFileObject::READ_CSV);
    }
}

Grâce à la capacité de décoration standard de TraversableIterator , le code constructeur d'origine du premier exemple de CSVFile n'a pu être copié qu'à 100%.

Ce dernier ajout permet également de conserver le code original qui utilise le CSVFile Iterator intact:

$csv = new CSVFile('../data/test.csv');

foreach ($csv as $line) {
    var_dump($line);
}

Donc, juste une refactorisation rapide pour permettre plus de réutilisation de code. Vous obtenez un KeyedArrayIterator gratuitement. 

31
hakre
$csv_data = array_map('str_getcsv', file('Book.csv'));// reads the csv file in php array
$csv_header = $csv_data[0];//creates a copy of csv header array
unset($csv_data[0]);//removes the header from $csv_data since no longer needed
foreach($csv_data as $row){
    $row = array_combine($csv_header, $row);// adds header to each row as key
    var_dump($row);//do something here with each row
}
4
Waqas Bukhary
function processCsv($absolutePath)
{
    $csv = array_map('str_getcsv', file($absolutePath));
    $headers = $csv[0];
    unset($csv[0]);
    $rowsWithKeys = [];
    foreach ($csv as $row) {
        $newRow = [];
        foreach ($headers as $k => $key) {
            $newRow[$key] = $row[$k];
        }
        $rowsWithKeys[] = $newRow;
    }
    return $rowsWithKeys;
}
2
Rupert

À ce stade, je suppose que vous avez déjà résolu le problème, mais que vous envisagez de résoudre le problème de la sorte, ce n’est probablement pas la solution la plus élégante/la plus élégante, mais c’est le cas:

$row = 1;
$array = array();
$marray = array();
$handle = fopen('file.csv', 'r');
if ($handle !== FALSE) {
    while (($data = fgetcsv($handle, 0, ',')) !== FALSE) {
        if ($row === 1) {
            $num = count($data);
            for ($i = 0; $i < $num; $i++) {
                array_Push($array, $data[$i]);
            }
        }
        else {
            $c = 0;
            foreach ($array as $key) {
                $marray[$row - 1][$key] = $data[$c];
                $c++;
            }
        }
        $row++;
    }
    echo '<pre>';
    print_r($marray);
    echo '</pre>';
}
1
Richard Porter

Essayez avec ce code:

$query = "SELECT * FROM datashep_AMS.COMPLETE_APPLICATIONS";
$export= mysql_query($query);
$first = true;
$temp = $export[0];
//echo "<pre>"; print_r($first); exit;

header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=file.csv');
header('Pragma: no-cache');
header("Expires: 0");

$outstream = fopen("php://output", "w");



foreach($export as $result)
{
    if($first){
        $titles = array();
        foreach($temp as $key=>$val){
            $titles[] = $key;
        }
        //print_r ($titles);exit;
        fputcsv($outstream, $titles);
    }
    $first = false;
    fputcsv($outstream, $result);
}

fclose($outstream);

Merci

0
Krunal Shah

Dans la réponse de Tim Cooper ci-dessus, au lieu de

$all_rows = array();
$header = null;
while ($row = fgetcsv($file)) {
    if ($header === null) {
        $header = $row;
        continue;
    }
    $all_rows[] = array_combine($header, $row);
}

Je coderais d'une manière plus élégante et efficace: 

$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
    $all_rows[] = array_combine($header, $row);
}
0
Pierre François

La fonction array_combine () ne fonctionne que si les colonnes d'en-tête correspondent aux colonnes de données, sinon une erreur est renvoyée.

0
PHP_

Essaye ça

$csv = array_map("str_getcsv", file('file.csv', FILE_SKIP_EMPTY_LINES));    
$header = array_shift($csv); // get header from array

foreach ($csv as $key => $value) {    
    $csv[$key] = array_combine($header, $value);
    var_dump($csv[$key]['Model']);
}

var_dump($csv);
0
Azam Alvi