Вопрос по arrays, php – Обработка CSV в массив с заголовками столбцов для ключа

18

У меня есть CSV с первой строкой, содержащей имена полей. Пример данных ...

<code>"Make","Model","Note"
"Chevy","1500","loaded"
"Chevy","2500",""
"Chevy","","loaded"
</code>

Мне нужно, чтобы мои данные были отформатированы в виде массива пар ключ-значение, где имя ключа является заголовком столбца. Я думаю, это будет что-то вроде этого для строки 1:

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

... строка 2 ...

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

... и строка 3 ...

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

Я не уверен, как это сделать, кроме как статически - проблема в том, что столбцы с соответствующими данными могут меняться от одного файла к следующему ... столбцы переставляются, удаляются или добавляются.

Ваши идеи очень ценятся.

вы, вероятно, имеете в виду 2500 во втором примере массива user1899415

Ваш Ответ

9   ответов
45
$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
  $all_rows[] = array_combine($header, $row);
}
print_r($all_rows);
обратите внимание, что при первом его создании вам нужно будет пропустить заголовок $, чтобы убедиться, что вы указали столбцы без фиктивных данных заголовков, таких как & quot; неизвестно & quot; , $ x или массив объединить будет разной длины
Спасибо за быстрый ответ. Это близко, хотя я заканчиваю с данными в ключах. Не видеть заголовки столбцов в любом из возвращаемых массивов. Bit Bucket
@BitBucket: если вы делаете дамп данных, которые находятся в$all_rows, вы должны увидеть массив с вложенными массивами, которые имеют данные заголовка в качестве ключей.
0

Попробуй это

$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);
2
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;
}
4
$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
}
0

array_combine () функция работает только в том случае, если столбцы заголовков соответствуют столбцам данных, в противном случае выдается ошибка.

Это должен быть комментарий к существующему ответу, а не сам ответ?
Спасибо, но подумал, что это может помочь сосредоточить решения; Это уже трудное место на эту тему - с таким количеством ответов. Я только упомянул это, чтобы избежать путаницы. Многие PHP самопровозглашенные «эксперты» опубликовали решения с использованием функции array_combine, но не заметили, что в ней есть потоки. Мое решение состоит в том, чтобы отредактировать заголовки файла CSV так, чтобы они соответствовали данным, а затем сохранить выходные данные перед использованием array_combine ...
0

Попробуйте с этим кодом:

$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);

Спасибо

31

PHP предлагает уже 99,9% того, что вам нужно в течениеSplFileObjectВы добавляете недостающие 0,1%, расширяя его. В следующем примереCSVFile вытекает из этого:

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

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

С данными вашего примера:

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 определяется как следующее:

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

Если вы сделаете это таким образом, все детали будут просто заключены в капсулу. Кроме того, более легко справляться с ошибками (например, несовпадением счетчиков) внутриcurrent() функция, поэтому код, который использует данные, не должен иметь дело с этим.

Edit:

Однако приведенный пример является коротким с точки зрения повторного использования. Вместо расширения отSplFileObject гораздо лучше его агрегировать:

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

Кодidentical но детали, которые были заключены в конструкторе, не учитываются. Это сокращение позволяет использовать тип более широко, например, с (но не только с) сказаннымSplFileObject:

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

$csv = new KeyedArrayIterator($file);

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

Если теперь это звучит слишком многословно, его снова можно обернуть, чтобы придать ему более приятный вид:

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

Благодаря стандартной способности украшенияTraversableIteratorисходный код конструктора из первого примераCSVFile можно просто скопировать на 100%.

Это последнее дополнение также позволяет сохранить оригинальный код, которыйuses CSVFile Итератор не поврежден:

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

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

Так что просто быстрый рефакторинг, чтобы позволить больше повторного использования кода. Вы получаетеKeyedArrayIterator бесплатно.

Только что проверил это, и мне нравится идея, но она кажется гораздо менее производительной, чем fgetcsv, как здесьstackoverflow.com/questions/4801895/csv-to-associative-array
Не могли бы вы подумать о том, как сделать этот дескриптор CSV без заголовка ради интереса?
Это довольно просто: опуститьrewind функциональные и передать ключи в конструкторе вместо. Если вам нужна большая гибкость, я поместил некоторый код в суть примеров, но он по-прежнему довольно альфа:gist.github.com/4153380
@ giorgio79: Этот подход, основанный на итераторах, более полезен, если вы не хотите хранить весь файл в памяти. Так что речь идет не столько о скорости, сколько о памяти. Так что, просто показывая альтернативные способы, у каждого есть свои плюсы и минусы.
очень хорошо......
0

В ответе Тим Купер выше, а не

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

Я бы написал более элегантным и эффективным способом:

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

На данный момент я предполагаю, что вы уже решили проблему, но подумал, что я предложу способ обойти это, вероятно, не самое лучшее / самое изящное решение, но оно делает свое дело:

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

Похожие вопросы