将 CSV 处理为带有键列标题的数组
Posted
技术标签:
【中文标题】将 CSV 处理为带有键列标题的数组【英文标题】:Process CSV Into Array With Column Headings For Key 【发布时间】:2012-04-28 04:36:23 【问题描述】:我有一个 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"
];
除了静态之外,我不确定如何执行此操作 - 问题是具有相关数据的列可能会从一个文件更改为下一个文件...重新排列、删除或添加列。
非常感谢您的想法。
【问题讨论】:
你可能是指第二个数组示例中的 2500 【参考方案1】:$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file))
$all_rows[] = array_combine($header, $row);
print_r($all_rows);
【讨论】:
感谢您的及时回复。尽管我最终得到了键中的数据,但这很接近。在返回的任何数组中都看不到任何列标题。 @BitBucket:如果您转储$all_rows
中的数据,您应该会看到一个包含以标题数据作为键的子数组的数组。
请注意,您需要在第一次创建 $header 时运行它,以确保您给任何没有标题的列提供像 "unknown" 这样的虚拟数据。 $x 或数组组合将是不同的长度【参考方案2】:
php 已经在SplFileObject
中提供了您所需的 99.9%,您可以通过扩展它来添加缺少的 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()
函数中处理错误(例如计数不匹配)更容易,因此使用数据的代码不需要处理它。
编辑:
但是,给出的示例在可重用性方面很短。与其从 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。
【讨论】:
为了感兴趣,你能想出一种方法来制作这个无标题的 CSV 吗? 这很简单:省略rewind
函数并在构造函数中传递键。如果您需要更多的灵活性,我已经将一些代码放入带有示例的要点中,但它仍然是非常好的 alpha:gist.github.com/4153380
刚刚测试过这个,我喜欢这个想法,但它的性能似乎比 fgetcsv 像这里 ***.com/questions/4801895/csv-to-associative-array
@giorgio79:好吧,如果您不想将整个文件存储在内存中,这种基于迭代器的方法更有用。因此,它与速度无关,而与内存有关。所以只是展示替代方法,每一种都有它的优点/缺点。
我知道这是一篇很老的帖子,但我最近在一个项目中使用它并取得了巨大的成功,所以谢谢。我遇到的一个问题是无法使用 SKIP_EMPTY 标志。有没有办法添加它?【参考方案3】:
$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】: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;
【讨论】:
【参考方案5】:在这一点上,我假设您已经解决了这个问题,但我想我会提出一个建议的方法来解决这个问题,可能不是最好/最优雅的解决方案,但它可以解决问题:
$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>';
【讨论】:
【参考方案6】:试试这个代码:
$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);
谢谢
【讨论】:
【参考方案7】:试试这个
$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);
【讨论】:
【参考方案8】:array_combine() 函数仅在标题列与数据列匹配时才有效,否则将引发错误。
【讨论】:
这应该是对现有答案的评论,而不是自己的答案? 谢谢,但认为这可能有助于集中解决方案;在这个主题上已经是一个紧张的地方 - 有这么多的答案。我只是为了避免混淆而提到它。许多自称为“专家”的 PHP 已经发布了使用 array_combine 函数的解决方案,但没有注意到其中有流。我的解决方案是编辑 CSV 文件头以匹配数据,然后在使用 array_combine 之前存储输出... @Pellumb:除非是故意的错误。如果您正在运行大多数(如果不是全部)示例(即使是那些不使用 array_combine() ,但尤其是那些没有的)没有正确处理 CSV 文件/缓冲区/流中实际标题列之前的 cmets。另一个常见问题是末尾有一个(或多个)终止符行(array_combine() 也会用错误消息突出显示)。【参考方案9】:在上面蒂姆库珀的回答中,而不是
$all_rows = array();
$header = null;
while ($row = fgetcsv($file))
if ($header === null)
$header = $row;
continue;
$all_rows[] = array_combine($header, $row);
我会以更优雅、更高效的方式编写代码:
$rows = null;
$header = fgetcsv($file);
while ($row = fgetcsv($file))
$rows[] = array_combine($header, $row);
【讨论】:
以上是关于将 CSV 处理为带有键列标题的数组的主要内容,如果未能解决你的问题,请参考以下文章
为啥将 numpy 数组转换为 csv 文件不显示属性名称,而是将第一行值作为属性名称?