it-swarm.com.de

CSV in Array mit Spaltenüberschriften für Schlüssel verarbeiten

Ich habe eine CSV mit der ersten Zeile, die die Feldnamen enthält. Beispieldaten sind ...

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

Ich brauche meine Daten in einem Array von Schlüssel-Wert-Paaren, wobei der Schlüsselname die Spaltenüberschrift ist. Ich denke, für Zeile 1 würde es so aussehen:

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

... Reihe 2 ...

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

... und Reihe 3 ...

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

Ich bin nicht sicher, wie dies anders als statisch zu tun ist - das Problem ist, dass die Spalten mit den zugehörigen Daten von einer Datei zur nächsten wechseln könnten ... Spalten wurden umgeordnet, gelöscht oder hinzugefügt.

Ihre Ideen werden sehr geschätzt.

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 bietet bereits 99,9% von dem, was Sie in SplFileObject benötigen, und fügen Sie die fehlenden 0,1% hinzu, indem Sie es erweitern. Im folgenden Beispiel geht CSVFile davon aus:

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

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

Mit Ihren Beispieldaten:

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 ist wie folgt definiert:

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;
    }
}

Wenn Sie dies auf diese Weise tun, werden die Details gut gekapselt. Außerdem ist es einfacher, mit Fehlern (z. B. Count Mismatch) in der Funktion current() umzugehen, so dass der Code, der die Daten verwendet, sich nicht damit befassen muss.

Editieren:

Das gegebene Beispiel ist jedoch in Bezug auf die Wiederverwendbarkeit kurz. Anstatt von SplFileObject zu erweitern, ist es viel besser, es zu aggregieren:

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;
    }
}

Der Code ist identisch , aber die Details, die im Konstruktor gekapselt wurden, werden weggelassen. Diese Reduktion ermöglicht die breitere Verwendung des Typs, z. mit (aber nicht nur mit) dem besagten SplFileObject :

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

$csv = new KeyedArrayIterator($file);

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

Wenn das jetzt zu viel klingt, kann es wieder umwickelt werden, um ihm eine schönere Fassade zu geben:

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

Dank der Standard-Dekorierbarkeit von TraversableIterator konnte der ursprüngliche Konstruktorcode aus dem ersten Beispiel von CSVFile nur zu 100% kopiert werden.

Dieser letzte Zusatz erlaubt es auch, den ursprünglichen Code, den verwendet, um the CSVFile Iterator intakt zu halten:

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

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

Also nur ein kurzes Refactoring, um mehr Code-Wiederverwendung zu ermöglichen. Sie erhalten einen KeyedArrayIterator kostenlos. 

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

An dieser Stelle gehe ich davon aus, dass Sie das Problem bereits gelöst haben, aber ich dachte, ich würde dies in einer vorgeschlagenen Weise tun, wahrscheinlich nicht die beste/eleganteste Lösung, aber es hat den Trick:

$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

Versuchen Sie es mit diesem 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);

Vielen Dank

0
Krunal Shah

In der Antwort von Tim Cooper oben statt

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

Ich würde auf eine elegantere und effizientere Weise codieren: 

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

Die Funktion array_combine () funktioniert nur, wenn die Kopfspalten mit den Datenspalten übereinstimmen. Andernfalls wird ein Fehler ausgegeben.

0
PHP_

Versuche dies

$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