7

I have a PHP routine that reads a .csv file that has been uploaded to my site.

The number of fields in the file may be different from upload to upload.

I want to be able to establish the size of the .csv file (the number of fields) and then store its contents in an array.

This is what I have so far:

//get the csv file 
$file = $_FILES[csv1][tmp_name]; 
$handle = fopen($file,"r"); 

//loop through the csv file and insert into array
$dataline = fgetcsv($handle,1000,",","'");
$fieldnum = count($dataline);

$recordloop = 0;

while ($dataline) 
{ 

    for ($fieldloop=0; $fieldloop <$fieldnum; $fieldloop++)
    {
        //this is the bit that is wrong
        $dataarray[]=array($dataline[$fieldloop]);
    }

    $data = fgetcsv($handle,1000,",","'");
    $recordloop++;
}

If for example, there are 30 fields and 200 records I am trying to get the array to be of the structure $dataarray[200][30].

How do I get the data into the array of this structure?

Example .csv:

Row 1 will be field headings
Name, Gender, Ethnicity, DOB, etc .... the number of fields is not fixed - it could be from 30 to 40 fields in this row

Rows 2 onwards will be the records
John, M, British, 10/04/49, etc
Simon, M, British, 04/03/65, etc

ANSWER - did just what I wanted

$file = $_FILES[csv1][tmp_name]; 
$handle = fopen($file,"r"); 

$matrix = array();
while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) 
{
$matrix[] = $row;
}
RGriffiths
  • 5,722
  • 18
  • 72
  • 120
  • can you edit with a csv sample? – Adrian Cid Almaguer Apr 17 '15 at 23:34
  • 1
    Check out http://stackoverflow.com/questions/17761172/php-csv-string-to-array or http://stackoverflow.com/questions/4732953/how-to-parse-csv-in-php-having-multiline-data-in-a-column or http://stackoverflow.com/questions/2805427/how-to-extract-data-from-csv-file-in-php or http://stackoverflow.com/questions/3930061/parse-csv-file-php. Take your pick. – But those new buttons though.. Apr 18 '15 at 01:26

3 Answers3

20

You already have this kind of implementation in PHP:

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

Explanation:

  • str_getcsv() - Parse a CSV string into an array.
  • file() - Reads entire file into an array.
  • array_map(Function, Array)- Returns an array containing all the elements of the array after applying the callback function to each one.

Example output:

//CSV
NAME,CLASS,ROOM
Sally,2018,312
Belinda,2017,148

//OUTPUT:
Array(
       [0] => Array([0] => NAME,    [1] => CLASS, [2] => ROOM), 
       [1] => Array([0] => Sally,   [1] => 2018,  [2] => 312),
       [2] => Array([0] => Belinda, [1] => 2017,  [2] => 148)
)

Associative keys

Two quick functions to parse with associative keys - both based on the array_map method:

METHOD 1: running twice

function assoc_getcsv($csv_path) {
    $r = array_map('str_getcsv', file($csv_path));
    foreach( $r as $k => $d ) { $r[$k] = array_combine($r[0], $r[$k]); }
    return array_values(array_slice($r,1));
}  

METHOD 2: running once

function assoc_getcsv($csv_path) {
    $f = array();
    function parse_csv_assoc($str,&$f){ 
        if (empty($f)) { $f = str_getcsv($str); }
        return array_combine($f, str_getcsv($str));         
    }
    return array_values(array_slice(array_map('parse_csv_assoc', file($csv_path), $f),1));
} 

Example output:

//CSV
NAME,CLASS,ROOM
Sally,2018,312
Belinda,2017,148

//OUTPUT:
Array(
       [0] => Array([NAME] => Sally,   [CLASS] => 2018,  [ROOM] => 312),
       [1] => Array([NAME] => Belinda, [CLASS] => 2017,  [ROOM] => 148)
)
Shlomi Hassid
  • 6,500
  • 3
  • 27
  • 48
  • 1
    That's an interestingly short way of getting the job done. Just out of curiosity - have you compared the speed of this and working with `SplFileObject` and an `Iterator`? – tftd Apr 18 '15 at 01:39
  • No -> I'm using this function for month and parsing 11K CSV in ~1-2secs – Shlomi Hassid Apr 18 '15 at 01:44
  • This definitely is twice as fast as using an `SplFileObject`. Here's my [test](https://gist.github.com/steve-todorov/929c074788011aff3230). – tftd Apr 18 '15 at 01:53
  • @tftd great test! are you up to test the two other methods too? – Shlomi Hassid Apr 18 '15 at 03:08
4

this function will read a CSV and generate a multi-dimensional array

function fromCSVFile( $file) {
    // open the CVS file
    $handle = @fopen( $file, "r");
    if ( !$handle ) {
        throw new \Exception( "Couldn't open $file!" );
    }

    $result = [];

    // read the first line
    $first = strtolower( fgets( $handle, 4096 ) );
    // get the keys
    $keys = str_getcsv( $first );

    // read until the end of file
    while ( ($buffer = fgets( $handle, 4096 )) !== false ) {

        // read the next entry
        $array = str_getcsv ( $buffer );
        if ( empty( $array ) ) continue;

        $row = [];
        $i=0;

        // replace numeric indexes with keys for each entry
        foreach ( $keys as $key ) {
            $row[ $key ] = $array[ $i ];
            $i++;
        }

        // add relational array to final result
        $result[] = $row;
    }

    fclose( $handle );
    return $result;
}

So if your .CSV file looks like this:

Name,Gender,Ethnicity,DOB
John,M,British,10/04/49
Simon,M,British,04/03/65

You will get this:

(
    [0] => Array
        (
            [name] => John
            [gender] => M
            [ethnicity] => British
            [dob] => 10/04/49
        )

    [1] => Array
        (
            [name] => Simon
            [gender] => M
            [ethnicity] => British
            [dob] => 04/03/65
        )

)
Andrew Kapunin
  • 406
  • 4
  • 9
  • 1
    Thanks, and that is great but I don't actually want the first row to become the key. I want them to remain in the first row of the array. – RGriffiths Apr 18 '15 at 06:18
4

1.csv:

Name,Gender,Ethnicity,DOB
John,M,British,10/04/49
Simon,M,British,04/03/65

Use file() to read the csv file into an array, then iterate through this array and get all the fields into an array element with explode() and make your new $array

<?php

$file = '1.csv';
$content = file($file);

$array = array();

for($i = 1; $i < count($content); $i++) {
    $line = explode(',', $content[$i]);
    for($j = 0; $j < count($line); $j++) {
        $array[$i][$j + 1] = $line[$j];
    }   
}   

print_r($array);

?>

Output:

Array
(
    [1] => Array
        (
            [1] => John
            [2] => M
            [3] => British
            [4] => 10/04/49

        )

    [2] => Array
        (
            [1] => Simon
            [2] => M
            [3] => British
            [4] => 04/03/65

        )

)

If you want get record:2 and field:4 you can do this:

echo $array[2][4];

Output:

04/03/65
Adrian Cid Almaguer
  • 7,815
  • 13
  • 41
  • 63