2014-09-24 4 views
1

issue: У меня есть ассоциативный массив, где все ключи представляют собой заголовки csv и значения в каждом массиве $ key => .. представляют элементы в этом столбце.PHP - массив в CSV по столбцу

Исследование: Насколько мне известно, fputcsv любит ходить в построчно, но этот массив столбцов на основе делает сложно. Я не нашел никакой функции, которая выполняет это.

Пример:

Array(
    ['fruits'] => Array(
     [0] => 'apples', 
     [1] => 'oranges', 
     [2] => 'bananas' 
    ), 
    ['meats'] => Array(
     [0] => 'porkchop', 
     [1] => 'chicken', 
     [2] => 'salami', 
     [3] => 'rabbit' 
    ), 
) 

должно стать:

fruits,meats 
apples,porkchop 
oranges,chicken 
bananas,salami 
,rabbit 

Почему это трудно:

Вы должны знать максимальное количество строк, чтобы сделать пустым пятна.

ответ

2

Мне пришлось написать свою собственную функцию. Думал, может быть, это может помочь кому-то другому!

/* 
* The array is associative, where the keys are headers 
* and the values are the items in that column. 
* 
* Because the array is by column, this function is probably costly. 
* Consider a different layout for your array and use a better function. 
* 
* @param $array array The array to convert to csv. 
* @param $file string of the path to write the file. 
* @param $delimeter string a character to act as glue. 
* @param $enclosure string a character to wrap around text that contains the delimeter 
* @param $escape string a character to escape the enclosure character. 
* @return mixed int|boolean result of file_put_contents. 
*/ 

function array_to_csv($array, $file, $delimeter = ',', $enclosure = '"', $escape = '\\'){ 
    $max_rows = get_max_array_values($array); 
    $row_array = array(); 
    $content = ''; 
    foreach ($array as $header => $values) { 
    $row_array[0][] = $header; 
    $count = count($values); 
    for ($c = 1; $c <= $count; $c++){ 
     $value = $values[$c - 1]; 
     $value = preg_replace('#"#', $escape.'"', $value); 
     $put_value = (preg_match("#$delimeter#", $value)) ? $enclosure.$value.$enclosure : $value; 
     $row_array[$c][] = $put_value; 
    } 
    // catch extra rows that need to be blank 
    for (; $c <= $max_rows; $c++) { 
     $row_array[$c][] = ''; 
    } 
    } 
    foreach ($row_array as $cur_row) { 
    $content .= implode($delimeter,$cur_row)."\n"; 
    } 
    return file_put_contents($file, $content); 
} 

И это:

/* 
* Get maximum number of values in the entire array. 
*/ 
function get_max_array_values($array){ 
    $max_rows = 0; 
    foreach ($array as $cur_array) { 
    $cur_count = count($cur_array); 
    $max_rows = ($max_rows < $cur_count) ? $cur_count : $max_rows; 
    } 
    return $max_rows; 
} 

Новый способ (с использованием класса)

я написал класс для этого какое-то время спустя, что я буду предоставлять для тех, кто ищет один сейчас:

class CSVService { 

    protected $csvSyntax; 

    public function __construct() 
    { 
     return $this; 
    } 

    public function renderCSV($contents, $filename = 'data.csv') 
    { 
     header('Content-type: text/csv'); 
     header('Content-Disposition: attachment; filename="' . $filename . '"'); 

     echo $contents; 
    } 

    public function CSVtoArray($filename = '', $delimiter = ',') { 
     if (!file_exists($filename) || !is_readable($filename)) { 
      return false; 
     } 

     $headers = null; 
     $data = array(); 
     if (($handle = fopen($filename, 'r')) !== false) { 
      while (($row = fgetcsv($handle, 0, $delimiter, '"')) !== false) { 
       if (!$headers) { 
        $headers = $row; 
        array_walk($headers, 'trim'); 
        $headers = array_unique($headers); 
       } else { 
        for ($i = 0, $j = count($headers); $i < $j; ++$i) { 
         $row[$i] = trim($row[$i]); 
         if (empty($row[$i]) && !isset($data[trim($headers[$i])])) { 
          $data[trim($headers[$i])] = array(); 
         } else if (empty($row[$i])) { 
          continue; 
         } else { 
          $data[trim($headers[$i])][] = stripcslashes($row[$i]); 
         } 
        } 
       } 
      } 
      fclose($handle); 
     } 
     return $data; 
    } 

    protected function getMaxArrayValues($array) 
    { 
     return array_reduce($array, function($carry, $item){ 
      return ($carry > $c = count($item)) ? $carry : $c; 
     }, 0); 
    } 

    private function getCSVHeaders($array) 
    { 
     return array_reduce(
       array_keys($array), 
       function($carry, $item) { 
        return $carry . $this->prepareCSVValue($item) . $this->csvSyntax->delimiter; 
       }, '') . "\n"; 
    } 

    private function prepareCSVValue($value, $delimiter = ',', $enclosure = '"', $escape = '\\') 
    { 
     $valueEscaped = preg_replace('#"#', $escape . '"', $value); 
     return (preg_match("#$delimiter#", $valueEscaped)) ? 
       $enclosure . $valueEscaped . $enclosure : $valueEscaped; 
    } 

    private function setUpCSVSyntax($delimiter, $enclosure, $escape) 
    { 
     $this->csvSyntax = (object) [ 
      'delimiter' => $delimiter, 
      'enclosure' => $enclosure, 
      'escape' => $escape, 
     ]; 
    } 

    private function getCSVRows($array) 
    { 
     $n = $this->getMaxArrayValues($array); 
     $even = array_values(
      array_map(function($columnArray) use ($n) { 
       for ($i = count($columnArray); $i <= $n; $i++) { 
        $columnArray[] = ''; 
       } 
       return $columnArray; 
      }, $array) 
     ); 

     $rowString = ''; 

     for ($row = 0; $row < $n; $row++) { 
      for ($col = 0; $col < count($even); $col++) { 
       $value = $even[$col][$row]; 
       $rowString .= 
         $this->prepareCSVValue($value) . 
         $this->csvSyntax->delimiter; 
      } 
      $rowString .= "\n"; 
     } 

     return $rowString; 
    } 

    public function arrayToCSV($array, $delimiter = ',', $enclosure = '"', $escape = '\\', $headers = true) { 
     $this->setUpCSVSyntax($delimiter, $enclosure, $escape); 

     $headersString = ($headers) ? $this->getCSVHeaders($array) : ''; 

     $rowsString = $this->getCSVRows($array); 


     return $headersString . $rowsString; 
    } 

} 
+0

Работайте отлично. Примите свой ответ. –

+0

Я жду, прежде чем принять свой собственный ответ - потому что у кого-то может быть лучше: D – amurrell

0
$data = array(
    'fruits' => array(
     'apples', 
     'oranges', 
     'bananas' 
    ), 
    'meats' => array(
     'porkchop', 
     'chicken', 
     'salami', 
     'rabbit' 
    ), 
); 

$combined = array(array('fruits', 'meats')); 

for($i = 0; $i < max(count($data['fruits']), count($data['meats'])); $i++) 
{ 
    $row = array(); 

    $row[] = isset($data['fruits'][$i]) ? $data['fruits'][$i] : ''; 
    $row[] = isset($data['meats'][$i]) ? $data['meats'][$i] : ''; 

    $combined[] = $row; 
} 

ob_start(); 

$fp = fopen('php://output', 'w'); 

foreach($combined as $row) 
    fputcsv($fp, $row); 

fclose($fp); 

$data = ob_get_clean(); 

var_dump($data); 

Преобразование в csv и синтаксический анализ массива могут выполняться в одном цикле. Или вы имели в виду, что может быть больше столбцов? Этот код можно легко изменить для общего типа массива. Например, для любого числа столбцов

$data = array(
    'fruits' => array(
     'apples', 
     'oranges', 
     'bananas' 
    ), 
    'meats' => array(
     'porkchop', 
     'chicken', 
     'salami', 
     'rabbit' 
    ), 
); 
$heads = array_keys($data); 
$maxs = array(); 
foreach($heads as $head) 
    $maxs[] = count($data[$head]); 
ob_start(); 
$fp = fopen('php://output', 'w'); 
fputcsv($fp, $heads); 
for($i = 0; $i < max($maxs); $i++) 
{ 
    $row = array(); 
    foreach($heads as $head) 
     $row[] = isset($data[$head][$i]) ? $data[$head][$i] : ''; 
    fputcsv($fp, $row); 
} 
fclose($fp); 
$data = ob_get_clean(); 
var_dump($data); 
+0

Теперь вопрос заключается в том, что одно из этих решений менее дорогостоящее/более эффективное - особенно с огромными массивами (много столбцов). – amurrell

+0

@amurrell мой код должен работать с огромными массивами без проблемы, промежуточные данные не хранятся нигде, никаких регулярных выражений. одна вещь, которую можно улучшить, - это переместить max() из цикла в отдельную переменную. он просто читает строку за строкой, заменяет отсутствующие элементы пустой строкой и преобразует каждую строку в строку csv, ничего больше. буферизация вывода предназначена только для хранения строки csv в переменной, которую вы можете сохранить непосредственно в файл без нее. – Cheery

Смежные вопросы