将 CSV 处理为数组,其中包含键的列标题

2022-08-30 15:23:26

我有一个 CSV,第一行包含字段名称。示例数据是...

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

我需要将数据格式化为键值对数组,其中键名是列标题。我猜第1行会是这样的:

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

...第 2 行...

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

...和第 3 行...

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

除了静态之外,我不确定如何做到这一点 - 问题是具有关联数据的列可能会从一个文件更改为下一个文件...重新排列、删除或添加列。

非常感谢您的想法。


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

答案 2

PHP已经提供了99,9%的需求,您可以通过扩展来添加缺少的0,1%。在下面的示例中,从它扩展:SplFileObjectCSVFile

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

编辑:

然而,给出的例子在可重用性方面很短。与其从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;
    }
}

代码是相同的,但省略了构造函数中封装的详细信息。这种减少允许更广泛地使用该类型,例如,与(但不仅仅是)所述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%复制。

最后一个添加还允许保持使用 CSVFile 迭代器的原始代码不变:

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

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

因此,只需快速重构即可实现更多的代码重用。您可以免费获得一个KeyedArrayIterator