64

I'm trying to parse a CSV string to an array in PHP. The CSV string has the following attributes:

Delimiter: ,
Enclosure: "
New line: \r\n

Example content:

"12345","Computers","Acer","4","Varta","5.93","1","0.04","27-05-2013"
"12346","Computers","Acer","5","Decra","5.94","1","0.04","27-05-2013"

When I try to parse it like this:

$url = "http://www.url-to-feed.com";
$csv = file_get_contents($url);
$data = str_getcsv($csv);
var_dump($data);

The last and first element are concatenated in one string:

[0]=> string(5) "12345"
...
[7]=> string(4) "0.04"
[8]=> string(19) "27-05-2013
"12346""

How can I fix this? Any help would be appreciated.

0

11 Answers 11

148

Do this:

$csvData = file_get_contents($fileName);
$lines = explode(PHP_EOL, $csvData);
$array = array();
foreach ($lines as $line) {
    $array[] = str_getcsv($line);
}
print_r($array);

It will give you an output like this:

Array
(
    [0] => Array
        (
            [0] => 12345
            [1] => Computers
            [2] => Acer
            [3] => 4
            [4] => Varta
            [5] => 5.93
            [6] => 1
            [7] => 0.04
            [8] => 27-05-2013
        )

    [1] => Array
        (
            [0] => 12346
            [1] => Computers
            [2] => Acer
            [3] => 5
            [4] => Decra
            [5] => 5.94
            [6] => 1
            [7] => 0.04
            [8] => 27-05-2013
        )

)

I hope this can be of some help.

Sign up to request clarification or add additional context in comments.

10 Comments

I'm legitimately curious, but is there a reason why this would be better than Gaui's simple one-line solution below, or are people just upvoting this because it's the selected answer?
This fails for multiline CSV fields
This can cause a rare problem, if you have a newline contained inside a value, then the PHP_EOL splits the value into 2 different array values.
fails if there is a comma in any of the fields
Use fgetcsv() only! Because explode(PHP_EOL, $csvData) don't parse line breaks in csv strings correctly! See the right answer below.
|
65

You should use fgetcsv. Since you cannot import a file as a stream because the csv is a variable, then you should spoof the string as a file by using php://temp or php://memory first:

$fp = fopen("php://temp", 'r+');
fputs($fp, $csvText);
rewind($fp);

Then you will have no problem using fgetcsv:

$csv = [];
while ( ($data = fgetcsv($fp) ) !== FALSE ) {
    $csv[] = $data;
}
fclose($fp)

$data will be an array of a single csv line (which may include line breaks or commas, etc), as it should be.

Caveat: The memory limit of php://temp can be controlled by appending /maxmemory:NN, where NN is the maximum amount of data to keep in memory before using a temporary file, in bytes. (the default is 2 MB) http://www.php.net/manual/en/wrappers.php.php

7 Comments

This is one of the few answers that actually properly handles newlines inside of fields.. it should have far more upvotes.
This is the best way! Don't use file('data.csv') or explode(PHP_EOL, $csv) because they don't parse line breakes in csv strings correctly!
How does it distinguish between a newline inside a field and a newline constituting line/record delimiter?
I'm guessing it does this by figuring out if the line break is wrapped in the enclosure (usually ") and treating it appropriately.
This is the more proper way, the accepted answer is a quick way but doesn't cover multi lines in the fields
|
35

Handy oneliner:

$csv = array_map('str_getcsv', file('data.csv'));

8 Comments

Fails for multiline CSV fields, doesn't it?
Won't be able to handle new lines inside fields. The accepted answer solves for that with PHP_EOL. Good if you don't have new lines I suppose. Thanks for your effort.
Should credit the author of this handy one liner: php.net/manual/en/function.str-getcsv.php (see first comment)
Use fgetcsv() only! Because file('data.csv') don't parse line breaks in csv strings correctly! See the right answer below.
@Alex in the documentation of the method secure.php.net/manual/en/function.str-getcsv.php there are details on how to change the delimiter (to use semicolon instead of commas, for instance), as well as the enclosure and escape. So the method is equally valid, only that you need to dig a bit more and read the documentation. PHP documentation is quite nicely written, and has tones of examples.
|
10

I have used the following function to parse csv string to an associative array

public function csvToArray($file) {
    $rows = array();
    $headers = array();
    if (file_exists($file) && is_readable($file)) {
        $handle = fopen($file, 'r');
        while (!feof($handle)) {
            $row = fgetcsv($handle, 10240, ',', '"');
            if (empty($headers))
                $headers = $row;
            else if (is_array($row)) {
                array_splice($row, count($headers));
                $rows[] = array_combine($headers, $row);
            }
        }
        fclose($handle);
    } else {
        throw new Exception($file . ' doesn`t exist or is not readable.');
    }
    return $rows;
}

if your csv file name is mycsv.csv then you call this function as:

$dataArray = csvToArray("mycsv.csv");

you can get this script also in http://www.scriptville.in/parse-csv-data-to-array/

1 Comment

This is the best solution so far! Since I am using Excel CSV, I have changed this line $row = fgetcsv($handle, 10240, ',', '"'); to $row = fgetcsv($handle, 10240, ';', '"');
3

Slightly shorter version, without unnecessary second variable:

$csv = <<<'ENDLIST'
"12345","Computers","Acer","4","Varta","5.93","1","0.04","27-05-2013"
"12346","Computers","Acer","5","Decra","5.94","1","0.04","27-05-2013"
ENDLIST;

$arr = explode("\n", $csv);
foreach ($arr as &$line) {
  $line = str_getcsv($line);
}

1 Comment

Fails for multiline fields.
2

A modification of previous answers using array_map.
Blow up the CSV data with multiple lines.

$csv = array_map('str_getcsv', explode("\n", $csvData));

Comments

2

Try this, it's working for me:

$delimiter = ",";
  $enclosure = '"';
  $escape = "\\" ;
  $rows = array_filter(explode(PHP_EOL, $content));
  $header = NULL;
  $data = [];

  foreach($rows as $row)
  {
    $row = str_getcsv ($row, $delimiter, $enclosure , $escape);

    if(!$header) {
      $header = $row;
    } else {
      $data[] = array_combine($header, $row);
    }
  }

1 Comment

array_combine(): Both parameters should have an equal number of elements, how can I correct and checking if I have the same quantity, they are 2 associative arrays
0

If you need assign hardcoded keys for each row of data, you can use array_map() like this:

$example = array_map(
    function($v) {
        $column = str_getcsv($v, ";");
        return array(
            "foo" => $column[0],
            "bar" => $column[1]
        );
    },
    file('file.csv')
);

Comments

-1

It`s my written flexible solution (Reference), and will work for all separated values strings, concerning the new lines in the fields too...

function ToCells($svString, $delimiter = ',', $enclosure = '"', $eol = "\n") :array {
    $rows = [];
    $length = strlen($svString);
    $index = 0;
    while ($index < $length) {
        $row = [];
        $column = '';
        $inEnclosure = false;
        do {
            $char = $svString[$index++];
            if ($inEnclosure) {
                if ($char == $enclosure) {
                    if ($index < $length) {
                        $char = $svString[$index];
                        if ($char == $enclosure) {
                            $column .= $char;
                            $index++;
                        } else { $inEnclosure = false; }
                    } else {
                        $inEnclosure = false;
                        $row[] = $column;
                        break;
                    }
                } else { $column .= $char; }
            } else if ($char == $enclosure) {
                if($index < $length) {
                    $char = $svString[$index++];
                    if ($char == $enclosure) { $column .= $char; }
                    else {
                        $inEnclosure = true;
                        $column .= $char;
                    }
                } else {
                    $row[] = $column;
                    break;
                }
            } else if ($char == $delimiter) {
                $row[] = $column;
                $column = '';
            } else if ($char == "\r") {
                if ($index < $length) {
                    $char = $svString[$index];
                    if ($char == $eol) { $index++; }
                }
                $row[] = $column;
                break;
            } else if ($char == $eol) {
                $row[] = $column;
                break;
            } else { $column .= $char; }

            if ($index == $length) {
                $row[] = $column;
                break;
            }
        } while ($index < $length);
        $rows[] = $row;
    }
    return $rows;
}

Just call the function and enjoy:

ToCells($csvString);

Enjoy...

Comments

-1

Based on https://www.php.net/manual/en/function.str-getcsv.php#117692

This creates an assoc array from a string, where the header value is used as the array's key.

Code:

function csv_multiline_to_array($csv_str, $bool_first_row_is_headers = TRUE) {
  $csv = array_map('str_getcsv', explode("\n", trim($csv_str)));
  array_walk($csv, function(&$a) use ($csv) {
    if (count($a) == count($csv[0])) {
      $a = array_combine($csv[0], $a);
    }
    else {
      // Error converting CSV to an array. Not the same number of elements.
      // Perform whatever action desired. Eg, return FALSE;
    }
  });
  if ($bool_first_row_is_headers) {
    array_shift($csv); // remove column header
  }
    
  return $csv;    
}

So, the following:

Name, Location, Type
Armstrong Middle School,Acadia Parish,Elementary
Branch Elementary School,Acadia Parish,Middle School

Results in this array:

[0]['Name'] = 'Armstrong Middle School'
   ['Location'] = 'Acadia Parish'
   ['Type'] = 'Elementary'

[1]['Name'] = 'Branch Elementary'
   ['Location'] = 'Acadia Parish'
   ['Type'] = 'Middle School'

4 Comments

str_getcsv into a multidimensional array in php Using die() to halt execution is not modern practice. Throwing an informative exception is more common. I don't know that I support using two loops here.
The die statement was meant as a placeholder, but I'll edit it to make it more clear.
Your function only supports a subset of CSV. A column in CSV file can contain a line feed and reading a file with fgetcsv will handle that but your function will return incorrect results. Not to mention possible memory issues with large files. For these reasons I wouldn't recommend it.
I agree that if you can read a file directly, then there are other methods which can be used to achieve similar results. This is just for a string you already have. I've used this exact function for quick-and-dirty ways of importing a CSV file for whatever purpose, even with very large amounts of data. Thought some might find it useful.
-2

If you have carriage return/line feeds within columns, str_getcsv will not work.

Try https://github.com/synappnz/php-csv

Use:

include "csv.php";
$csv = new csv(file_get_contents("filename.csv"));
$rows = $csv->rows();
foreach ($rows as $row)
{
  // do something with $row
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.