在PHP中将str_getcsv转换为多维数组

24

我有以下这样的csv数值:

$csv_data = "test,this,thing
             hi,there,this
             is,cool,dude
             have,fun";
我想将整个CSV字符串读入到一个多维数组中,以便我得到:
array(
   array(
      'test' => 'hi',
      'this' => 'there',
      'thing' => 'this'
   ),
   array(
      'test' => 'is',
      'this' => 'cool',
      'thing' => 'dude'
   ),
   array(
      'test' => 'have',
      'this' => 'fun',
      'thing' => ''
   )
);

我希望得到这样的输出,注意CSV的值是动态的。


CSV 数据的最后一行是否应该有逗号?如果行没有相同列数,那么似乎不是有效的 CSV 数据。或者代码需要考虑到这种列数变化的情况吗? - Wiseguy
就像,第一行将是数组的键,其他行则是其值。 在“thing”=>“”部分,那是正确的。 - PinoyStackOverflower
我明白,但CSV数据的最后一行是have,fun,只有两列,不像其他行那样有三列。我们需要考虑处理列数不正确的行吗? - Wiseguy
好的,我已经更新了我的答案以反映两种情况(列数相同或不同)。 - Wiseguy
3
这是我见过的“封闭”现象中最奇怪的之一。这是一个非常通用、有用的问题,涉及如何将CSV字符串解析为数组。我也遇到了类似的问题,这就是我来这里的原因。 - matt
我同意@matt的观点。我在Stack Overflow上看到过更少有用的内容被公开并永久保存。投票重新开放。 - mickmackusa
3个回答

39

假设CSV数据中的每一行拥有相同数量的列,这个方法应该可以正常工作。

$lines = explode("\n", $csv_data);
$head = str_getcsv(array_shift($lines));

$array = array();
foreach ($lines as $line) {
    $array[] = array_combine($head, str_getcsv($line));
}
如果每行的列数不固定(例如您的示例中,最后一行只有2列而不是3列),请使用以下循环:
foreach ($lines as $line) {
    $row = array_pad(str_getcsv($line), count($head), '');
    $array[] = array_combine($head, $row);
}

3
那么,对于包含单个字段中的 \n 的 csv 数据怎么办?在 csv 中通常使用引号将这些字段括起来,例如:"某个字段中的\n值"。因此,在解析时必须忽略这些 \n。您的“explode”方法会将其切断。 - Chris Pillen
@ChrisPillen 虽然这不是提问者的要求之一,但这是一个有效的观点。你认为最好的解决方法是什么?将CSV数据保存到 tmpfile() 中,然后使用 fgetcsv() 解析它?我应该在我的答案中添加一个例子吗? - Wiseguy
@Wiseguy 你可以使用正则表达式来解析数据,但是使用临时文件和 fgetcsv() 将更简单、更清晰。 - Stephen M. Harris

7
这里提供一个完整的解决方案:
$lines = explode("\n", $csv_data);
$formatting = explode(",", $lines[0]);
unset($lines[0]);
$results = array();
foreach ( $lines as $line ) {
   $parsedLine = str_getcsv( $line, ',' );
   $result = array();
   foreach ( $formatting as $index => $caption ) {
      if(isset($parsedLine[$index])) {
         $result[$formatting[$index]] = trim($parsedLine[$index]);
      } else {
         $result[$formatting[$index]] = '';
      }
   }
   $results[] = $result;
}

那么我们在做什么呢?
首先,您的CSV数据将被拆分成行数组,使用explode函数实现。
由于CSV中的第一行描述了数据格式,因此必须将其与实际数据行分开(使用explodeunset函数)。
为了存储结果,我们初始化一个新的数组($results)。
使用foreach函数逐行遍历数据。对于每一行:
- 使用PHP的str_getcsv函数解析该行。 - 初始化一个空的结果数组。 - 根据格式检查每行数据。添加单元格,并用空字符串填充缺失列。

它没有按我所期望的输出,谢谢您的答复 :) - PinoyStackOverflower
这个问题比我想象的要复杂得多。请检查修改后的答案。详细说明将随后提供。 - jsalonen
根据您的描述,这应该能回答问题。 - jsalonen

2
这里有一个非常干净简单的解决方案:
function parse_row($row) {
  return array_map('trim', explode(',', $row));
}

$rows   = str_getcsv($csv_data, "\n");
$keys   = parse_row(array_shift($rows));
$result = array();

foreach ($rows as $row) {
  $row = parse_row($row);
  $row = array_pad($row, 3, NULL);

  $result[] = array_combine($keys, $row);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接