Filesystem 函数
在线手册:中文 英文
PHP手册

fgetcsv

(PHP 4, PHP 5)

fgetcsv从文件指针中读入一行并解析 CSV 字段

说明

array fgetcsv ( int $handle [, int $length [, string $delimiter [, string $enclosure ]]] )

handle
一个由 fopen()popen()fsockopen() 产生的有效文件指针。
length (可选)
必须大于 CVS 文件内最长的一行。在 PHP 5 中该参数是可选的。如果忽略(在 PHP 5.0.4 以后的版本中设为 0)该参数的话,那么长度就没有限制,不过可能会影响执行效率。
delimiter (可选)
设置字段分界符(只允许一个字符),默认值为逗号。
enclosure (可选)
设置字段环绕符(只允许一个字符),默认值为双引号。该参数是在 PHP 4.3.0 中添加的。

fgets() 类似,只除了 fgetcsv() 解析读入的行并找出 CSV 格式的字段然后返回一个包含这些字段的数组。

fgetcsv() 出错时返回 FALSE,包括碰到文件结束时。

Note: CSV 文件中的空行将被返回为一个包含有单个 null 字段的数组,不会被当成错误。

Example #1 读取并显示 CSV 文件的整个内容

<?php
$row 
1;
$handle fopen("test.csv","r");
while (
$data fgetcsv($handle1000",")) {
    
$num count($data);
    echo 
"<p> $num fields in line $row: <br>\n";
    
$row++;
    for (
$c=0$c $num$c++) {
        echo 
$data[$c] . "<br>\n";
    }
}
fclose($handle);
?>

从 PHP 4.3.5 起,fgetcsv() 的操作是二进制安全的。

Note: 该函数对区域设置是敏感的。比如说 LANG 设为 en_US.UTF-8 的话,单字节编码的文件就会出现读取错误。

Note: 在读取在 Macintosh 电脑中或由其创建的文件时, 如果 PHP 不能正确的识别行结束符,启用运行时配置可选项 auto_detect_line_endings 也许可以解决此问题。

参见 explode()file()pack()fputcsv()

参数

handle

A valid file pointer to a file successfully opened by fopen(), popen(), or fsockopen().

length

Must be greater than the longest line (in characters) to be found in the CSV file (allowing for trailing line-end characters). It became optional in PHP 5. Omitting this parameter (or setting it to 0 in PHP 5.0.4 and later) the maximum line length is not limited, which is slightly slower.

delimiter

Set the field delimiter (one character only).

enclosure

Set the field enclosure character (one character only).

escape

Set the escape character (one character only). Defaults as a backslash.

返回值

Returns an indexed array containing the fields read.

Note:

A blank line in a CSV file will be returned as an array comprising a single null field, and will not be treated as an error.

Note: 在读取在 Macintosh 电脑中或由其创建的文件时, 如果 PHP 不能正确的识别行结束符,启用运行时配置可选项 auto_detect_line_endings 也许可以解决此问题。

fgetcsv() returns NULL if an invalid handle is supplied or FALSE on other errors, including end of file.

更新日志

版本 说明
5.3.0 The escape parameter was added
4.3.5 fgetcsv() is now binary safe
4.3.0 The enclosure parameter was added

范例

Example #2 Read and print the entire contents of a CSV file

<?php
$row 
1;
if ((
$handle fopen("test.csv""r")) !== FALSE) {
    while ((
$data fgetcsv($handle1000",")) !== FALSE) {
        
$num count($data);
        echo 
"<p> $num fields in line $row: <br /></p>\n";
        
$row++;
        for (
$c=0$c $num$c++) {
            echo 
$data[$c] . "<br />\n";
        }
    }
    
fclose($handle);
}
?>

注释

Note:

Locale setting is taken into account by this function. If LANG is e.g. en_US.UTF-8, files in one-byte encoding are read wrong by this function.

参见


Filesystem 函数
在线手册:中文 英文
PHP手册
PHP手册 - N: 从文件指针中读入一行并解析 CSV 字段

用户评论:

skorunka at seznam dot cz (07-Apr-2012 04:40)

Bug fix:

public function get() // get parsed data (string * array)
{
    return $this->data; // way to much to do here
}

jean dot marie dot comets at gmail dot com (06-Apr-2012 12:28)

Here's my class for CSV parsing.

Note that it reads the delimiter.

Class :

<?php
/**
 * Simple CSV reading class from PHP5.
 *
 * The GNU License.
 *
 * Copyright (c) 2012 Jean-Marie Comets
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * @author Jean-Marie Comets <jean.marie.comets@gmail.com>
 * @version 1.0
 *
 * @usage :
 * $csv = new Csv_Reader('foo.bar'); // instances class
 * echo $csv->dump(); // returns data (raw HTML)
 * var_dump($csv->get()); // returns data (2D array)
 */

class Csv_Reader
{
    private
$file;
    private
$delimiter;
    private
$data;
   
    public function
__construct($filename, $delimiter = null) // constructor
   
{
       
// load all lines of CSV file
       
       
$this->file = file($filename);
       
       
// if delimiter is not given, find it yourself
       
       
$this->delimiter = ($delimiter === null) ? $this->delim() : $delimiter;
       
       
// parse data with delimiter
       
       
$this->parse();
    }
   
    public function
__destruct() // destructor
   
{
       
// not much to do as a matter of a fact =)
   
}
   
    private function
delim() // used to initialize $this->delimiter
   
{
       
// specify allowed field delimiters and order by priority
       
       
$delimiters = array(
           
'comma'     => ',',
           
'semicolon' => ';',
           
'colon'     => ':',
           
'pipe'         => '|',
           
'tab'         => '\t'
       
);
       
       
// compare for each delimiter average count of non-empty cells (long)
       
       
$max_count = 0; $result = 0; // initialize
       
       
foreach($delimiters as $index => $delimiter)
        {
           
$average_count = 0; // initialize the average count
           
           
foreach($this->file as $row)
            {
               
// split with delimiter
               
               
$tabRow = explode($delimiter,$row);
               
               
// add count of non-empty cells
               
               
$average_count += count(array_filter($tabRow));
            }
           
           
// calculate average
           
           
$average_count /= count($this->file);
           
           
// the average count is bigger than the previous
           
           
if($average_count > $max_count)
            {
               
               
// change max average count
               
               
$max_count = $average_count;
               
               
// set result as current index
               
               
$result = $index;
            }
        }
       
       
// worst case scenario : returns the first delimiter which has the most priority
       
       
return $delimiters[$result];
    }
   
    private function
parse() // parse data with set delimiter : initialize $this->data
   
{
       
// initialize data
       
       
$this->data = array();
       
        foreach(
$this->file as $row)
        {
           
// parse row and put into $this->data
           
           
$this->data[] = explode($this->delimiter,$row);
        }
    }
   
    public function
get() // get parsed data (string * array)
   
{
        return
$data; // way to much to do here
   
}
   
    public function
dump() // dump as raw HTML table
   
{
       
$tbody = '<tbody>'; // start tbody
       
       
foreach($this->data as $row)
        {
           
           
$tbody .= '<tr>'; //beginning of new row
           
           
foreach($row as $cell)
            {
               
$tbody .= '<td>'.$cell.'</td>'; // add cell
           
}
           
           
$tbody .= '</tr>'; // end row
       
}
       
       
$tbody .= '</tbody>'; // end tbody
       
       
return '<table>'.$tbody.'</table>'; // encapsulate in table tags
   
}
}

michael dot arnauts at gmail dot com (14-Mar-2012 09:19)

fgetcsv seems to handle newlines within fields fine. So in fact it is not reading a line, but keeps reading untill it finds a \n-character that's not quoted as a field.

Example:

<?php
/* test.csv contains:
"col 1","col2","col3"
"this
is
having
multiple
lines","this not","this also not"
"normal record","nothing to see here","no data"
*/

$handle = fopen("test.csv", "r");
while ((
$data = fgetcsv($handle)) !== FALSE) {
   
var_dump($data);
}
?>

Returns:
array(3) {
  [0]=>
  string(5) "col 1"
  [1]=>
  string(4) "col2"
  [2]=>
  string(4) "col3"
}
array(3) {
  [0]=>
  string(29) "this
is
having
multiple
lines"
  [1]=>
  string(8) "this not"
  [2]=>
  string(13) "this also not"
}
array(3) {
  [0]=>
  string(13) "normal record"
  [1]=>
  string(19) "nothing to see here"
  [2]=>
  string(7) "no data"
}

This means that you can expect fgetcsv to handle newlines within fields fine. This was not clear from the documentation.

nick at atomicdesign dot net (13-Jan-2012 08:48)

I was getting a bytes exhausted error when iterating through a CSV file. ini_set('auto_detect_line_endings', 1); fixed it.

erelsgl at gmail dot com (09-May-2011 01:57)

Here is a function that accepts the path to a CSV file, and inserts all records to the given MySQL table, paying attention to the column names:

<?php
function csv_file_to_mysql_table($source_file, $target_table, $max_line_length=10000) {
    if ((
$handle = fopen("$source_file", "r")) !== FALSE) {
       
$columns = fgetcsv($handle, $max_line_length, ",");
        foreach (
$columns as &$column) {
           
$column = str_replace(".","",$column);
        }
       
$insert_query_prefix = "INSERT INTO $target_table (".join(",",$columns).")\nVALUES";
        while ((
$data = fgetcsv($handle, $max_line_length, ",")) !== FALSE) {
            while (
count($data)<count($columns))
               
array_push($data, NULL);
           
$query = "$insert_query_prefix (".join(",",quote_all_array($data)).");";
           
mysql_query($query);
        }
       
fclose($handle);
    }
}

function
quote_all_array($values) {
    foreach (
$values as $key=>$value)
        if (
is_array($value))
           
$values[$key] = quote_all_array($value);
        else
           
$values[$key] = quote_all($value);
    return
$values;
}

function
quote_all($value) {
    if (
is_null($value))
        return
"NULL";

   
$value = "'" . mysql_real_escape_string($value) . "'";
    return
$value;
}
?>

This assumes that the columns in the table have exactly the same name as the columns in the CSV file, except that the dots (".") are removed. This is because MySQL column names cannot contain dots.

ifedinachukwu at yahoo dot com (05-Apr-2011 01:43)

I had a csv file whose fields included data with line endings (CRLF created by hitting the carriage returns in html textarea). Of course, the LF in these fields was escaped by MySQL during the creation of the csv. Problem is I could NOT get fgetcsv to work correctly here, since each and every LF was regarded as the end of a line of the csv file, even when it was escaped!

Since what I wanted was to get THE FIRST LINE of the csv file, then count the number of fields by exploding on all unescaped commas, I had to resort to this:

<?php
/*
First five lines of csv: the 4th row has a line-break within  a data field. The LFs represent line-feeds or \n
1,okonkwo joseph,nil,2010-01-12 17:41:40LF
2,okafor john,cq and sulphonamides,2010-01-12 17:58:03LF
3,okoye andrew,lives with hubby in abuja,2011-03-30 13:39:19LF
4,okeke peter,In 2001\, had appendicectomy in AbaCR
\LF
In 2004\, had ELCS at a private hoapital in Lagos,2011-03-30 13:39:19LF
5,adewale chris,cq and sulphonamides,2010-01-12 17:58:03LF

*/

       
$fp = fopen('file.csv', 'r');
       
$i = 1;
       
$str='';
       
$srch='';
            while (
false !== ($char = fgetc($fp))) {
               
$str .= $char;//use this to collect the string for outputting
               
$srch .= $char;//use this to search for LF, possible preceded by \'
               
if(strlen($srch) > 2){
                   
$srch = substr($srch, 1);//ie trim off the first char
               
}
                if(
$i > 1 && $srch[1] == chr(10) && $srch[0] != '\\'){//chr(10) is LF, ie \n
                   
break;//if you get to the \n NOT preceded by \, that's the real line-ending, stop collecting the string;
               
}
       
           
$i++;
        }
        echo
$str;//should contain the first line as string

?>
Perhaps there exists a more elegant solution to this issue, in which case I'd be glad to know!

Anonymous (19-Feb-2011 01:23)

NOTE: i think the example in this page should be changed. I think the following line should be added to just inside the while loop:

if( $data === true ) die('PAY ATTENTION DUMMY');

The reasoning for this, is if the user happens to take out the seemingly "extra" brackets around the "$data" part, they will get a while bunch of rows where all they get is the value true instead of an array. This way, if the user borrows the code but then later on removes the seemingly "extra" brackets, they have something keeping them safe from themselves at least.

I say this because this just happened to some poorly designed code in a system I manage, that as a result caused a whole bunch of blank records to be imported into a database.

Anonymous (19-Feb-2011 12:52)

NOTE: the 5th parameter was apparently not added until 5.3.0 yet the documentation on this page fails to mention this.

maxime dot thomas at wascou dot org (06-Jan-2011 05:01)

Ok, I finally found a clean way to fix the delimiter limitation ( delimiter must be a character).

Try this as delimiter '\0'.

<?php
...
$data = fgetcsv($fh, 1000, '\0');
...
?>

It's the empty character.

Kjeld (10-Dec-2010 06:58)

The fgetcsv() is really great. CSV files are not as simple as they seem. They may span multiple lines, contain unmatched double quotes (like: 32" screen size) and lot of other surprising variants. My problem was that I had an ISO-8859-1 encoded file and fgetcsv() expects it to be UTF-8 encoded. I tried with a range of regular expressions I found on the Internet but none of them worked 100 %. So, if you haven't discovered it yet: fgetcsv() is king. My server doesn't have str_getcsv() so I had to figure out some way to use fgetcsv() for my ISO-8859-1 files.

I found the answer here: http://bugs.php.net/bug.php?id=48507. So I ended up with:

<?php
class utf8encode_filter extends php_user_filter
{
  function
filter($in, $out, &$consumed, $closing)
  {
    while (
$bucket = stream_bucket_make_writeable($in)) {
     
$bucket->data = utf8_encode($bucket->data);
     
$consumed += $bucket->datalen;
     
stream_bucket_append($out, $bucket);
    }
    return
PSFS_PASS_ON;
  }
}
?>

and

<?php
$fp
= @fopen($fileName, "r");
stream_filter_register("utf8encode", "utf8encode_filter")
  or die(
"Failed to register filter");
stream_filter_prepend($fp, "utf8encode");
?>

matasbi at gmail dot com (08-Dec-2010 04:22)

Parse from Microsoft Excel "Unicode Text (*.txt)" format:

<?php
function parse($file) {
    if ((
$handle = fopen($file, "r")) === FALSE) return;
    while ((
$cols = fgetcsv($handle, 1000, "\t")) !== FALSE) {
        foreach(
$cols as $key => $val ) {
           
$cols[$key] = trim( $cols[$key] );
           
$cols[$key] = iconv('UCS-2', 'UTF-8', $cols[$key]."\0") ;
           
$cols[$key] = str_replace('""', '"', $cols[$key]);
           
$cols[$key] = preg_replace("/^\"(.*)\"$/sim", "$1", $cols[$key]);
        }
        echo
print_r($cols, 1);
    }
}
?>

code at ashleyhunt dot co dot uk (04-Dec-2010 03:14)

I needed a function to analyse a file for delimiters and line endings prior to importing the file into MySQL using LOAD DATA LOCAL INFILE

I wrote this function to do the job, the results are (mostly) very accurate and it works nicely with large files too.
<?php
function analyse_file($file, $capture_limit_in_kb = 10) {
   
// capture starting memory usage
   
$output['peak_mem']['start']    = memory_get_peak_usage(true);

   
// log the limit how much of the file was sampled (in Kb)
   
$output['read_kb']                 = $capture_limit_in_kb;
   
   
// read in file
   
$fh = fopen($file, 'r');
       
$contents = fread($fh, ($capture_limit_in_kb * 1024)); // in KB
   
fclose($fh);
   
   
// specify allowed field delimiters
   
$delimiters = array(
       
'comma'     => ',',
       
'semicolon' => ';',
       
'tab'         => "\t",
       
'pipe'         => '|',
       
'colon'     => ':'
   
);
   
   
// specify allowed line endings
   
$line_endings = array(
       
'rn'         => "\r\n",
       
'n'         => "\n",
       
'r'         => "\r",
       
'nr'         => "\n\r"
   
);
   
   
// loop and count each line ending instance
   
foreach ($line_endings as $key => $value) {
       
$line_result[$key] = substr_count($contents, $value);
    }
   
   
// sort by largest array value
   
asort($line_result);
   
   
// log to output array
   
$output['line_ending']['results']     = $line_result;
   
$output['line_ending']['count']     = end($line_result);
   
$output['line_ending']['key']         = key($line_result);
   
$output['line_ending']['value']     = $line_endings[$output['line_ending']['key']];
   
$lines = explode($output['line_ending']['value'], $contents);
   
   
// remove last line of array, as this maybe incomplete?
   
array_pop($lines);
   
   
// create a string from the legal lines
   
$complete_lines = implode(' ', $lines);
   
   
// log statistics to output array
   
$output['lines']['count']     = count($lines);
   
$output['lines']['length']     = strlen($complete_lines);
   
   
// loop and count each delimiter instance
   
foreach ($delimiters as $delimiter_key => $delimiter) {
       
$delimiter_result[$delimiter_key] = substr_count($complete_lines, $delimiter);
    }
   
   
// sort by largest array value
   
asort($delimiter_result);
   
   
// log statistics to output array with largest counts as the value
   
$output['delimiter']['results']     = $delimiter_result;
   
$output['delimiter']['count']         = end($delimiter_result);
   
$output['delimiter']['key']         = key($delimiter_result);
   
$output['delimiter']['value']         = $delimiters[$output['delimiter']['key']];
   
   
// capture ending memory usage
   
$output['peak_mem']['end'] = memory_get_peak_usage(true);
    return
$output;
}
?>

Example Usage:
<?php
$Array
= analyse_file('/www/files/file.csv', 10);

// example usable parts
// $Array['delimiter']['value'] => ,
// $Array['line_ending']['value'] => \r\n
?>

Full function output:
Array
(
    [peak_mem] => Array
        (
            [start] => 786432
            [end] => 786432
        )

    [line_ending] => Array
        (
            [results] => Array
                (
                    [nr] => 0
                    [r] => 4
                    [n] => 4
                    [rn] => 4
                )

            [count] => 4
            [key] => rn
            [value] =>

        )

    [lines] => Array
        (
            [count] => 4
            [length] => 94
        )

    [delimiter] => Array
        (
            [results] => Array
                (
                    [colon] => 0
                    [semicolon] => 0
                    [pipe] => 0
                    [tab] => 1
                    [comma] => 17
                )

            [count] => 17
            [key] => comma
            [value] => ,
        )

    [read_kb] => 10
)

Enjoy!

Ashley

mustafa dot kachwala at gmail dot com (25-Oct-2010 09:59)

A simple function to return 2 Dimensional array by parsing a CSV file.

<?php
   
function get2DArrayFromCsv($file,$delimiter) {
        if ((
$handle = fopen($file, "r")) !== FALSE) {
           
$i = 0;
            while ((
$lineArray = fgetcsv($handle, 4000, $delimiter)) !== FALSE) {
                for (
$j=0; $j<count($lineArray); $j++) {
                   
$data2DArray[$i][$j] = $lineArray[$j];
                }
               
$i++;
            }
           
fclose($handle);
        }
        return
$data2DArray;
    }
?>

jack dot peterson at gmail dot com (06-Oct-2010 12:30)

If you receive data in the following format:

Time,Dataset1,Dataset2,
timestamp1,item 1 for dataset 1,item1 for dataset2
timestamp2,item 2 for dataset 1,item2 for dataset2

the following code will output a series of arrays grouped by column with the resulting format:
array (
[column 1 title] => array (
[timestamp1] => item1 for dataset1
[timestamp2] => item2 for dataset1
)

[column 2 title] => array (
[timestamp1] => item1 for dataset2
[timestamp2] => item2 for dataset2
)
)

<?php

# Open the File.
if (($handle = fopen("rawdata.csv", "r")) !== FALSE) {
   
# Set the parent multidimensional array key to 0.
   
$nn = 0;
    while ((
$data = fgetcsv($handle, 0, ",")) !== FALSE) {
       
# Count the total keys in the row.
       
$c = count($data);
       
# Populate the multidimensional array.
       
for ($x=0;$x<$c;$x++)
        {
           
$csvarray[$nn][$x] = $data[$x];
        }
       
$nn++;
    }
   
# Close the File.
   
fclose($handle);
}

// take the row'ified data and columnize the array
function columnizeArray($csvarray) {
   
$array = array();
    foreach(
$csvarray as $key=>$value) {
       
// reparse into useful array data.
       
if ($key == 0) {
            foreach (
$value AS $key2=>$value2) {
               
$array[$key2] = array();
               
$array[$key2][] = $value2;
            }
        }else if (
$key > 0){
            foreach (
$value as $key3=>$value3) {
               
$array[$key3][] = $value3;
            }
        }else{
        }
    }
    return
$array;
}
function
groupColumns($array = null) {
   
$array2 = array();
    foreach (
$array as $k=>$v) {
       
// procss each column
        // $k = column number
        // $v = array of rows
       
if ($k == 0) {}else{ // working on column 2 or higher
           
$array2[$v[0]] = array();
            foreach (
$array[0] as $k1=>$v1) {
                if (
$v1 > 0) { // ignore the column heading
                    // store the first column variable in as the key.
                    // Store the value associated with this item as the value.
                   
$array2[$v[0]][$v1] = $v[$k1];
                }
            }
     }
    }
    return
$array2;
}

$array2 = groupColumns(columnizeArray($csvarray));

print_r($array2);

?>

Xander (29-Sep-2010 09:42)

I had a problem with multibytes. File was windows-1250, script was UTF-8 and set_locale wasn't work so I made a simple and safe workaround:

<?php
$fc
= iconv('windows-1250', 'utf-8', file_get_contents($_FILES['csv']['tmp_name']));

           
file_put_contents('tmp/import.tmp', $fc);
           
$handle = fopen('tmp/import.tmp', "r");
           
$rows = array();
            while ((
$data = fgetcsv($handle, 0, ";")) !== FALSE) {

               
$rows[] = $data;

            }
           
fclose($handle);
           
unlink('tmp/import.tmp');
?>

I hope You will find it out usefull.
Sorry for my english.

cyrbil at hotmail dot fr (13-Jul-2010 10:30)

fgetcsv() cannot properly deal with files that containt double "  ...
So I've made a function that does it properly
It's work pretty nice ;) (and fast)
Create the array in 2.1324736 sec for a csv with 100 000 values.

<?php
$stream
=fopen('bdd.csv','r');
$test=stream_get_contents($stream);
// print the array of the csv file.
print_r(csvstring_to_array($test));

function
csvstring_to_array($string, $CSV_SEPARATOR = ';',
 
$CSV_ENCLOSURE = '"', $CSV_LINEBREAK = "\r\n") {
$array1 = array();
$array2= array();    
$array1=preg_split('#'.$CSV_LINEBREAK.'#',$string);
for(
$i=0;$i<count($array1);$i++){
for(
$o=0;$o<strlen($array1[$i]);$o++){
if(
preg_match('#^'.$CSV_ENCLOSURE.'#',
substr($array1[$i],$o))){
if(!
preg_match('#^"(([^'.
$CSV_ENCLOSURE.']*('.
$CSV_ENCLOSURE.$CSV_ENCLOSURE
.')?[^'.$CSV_ENCLOSURE.']*)*)'.
$CSV_ENCLOSURE.$CSV_SEPARATOR.'#i',substr($array1[$i],$o,
strlen($array1[$i])),$mot)){
$mot[1]=substr(substr($array1[$i],$o,strlen($array1[$i])),1,-1);
}
$o++;$o++;
}
else{if(!
preg_match('#^([^'.$CSV_ENCLOSURE.
$CSV_SEPARATOR.']*)'.$CSV_SEPARATOR.'#i',
substr($array1[$i],$o,strlen($array1[$i])),$mot)){
$mot[1]=substr($array1[$i],$o,strlen($array1[$i]));
}
}
$o=$o+strlen($mot[1]);
$array2[$i][]=str_replace($CSV_ENCLOSURE.$CSV_ENCLOSURE,
$CSV_ENCLOSURE,$mot[1]);
}
}
return
$array2;
}
?>

do not spam aleske at live dot ru (08-Jul-2010 02:38)

The PHP's CSV handling stuff is non-standard and contradicts with RFC4180, thus fgetcsv() cannot properly deal with files like this example from Wikipedia:

1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00
1996,Jeep,Grand Cherokee,"MUST SELL!
air, moon roof, loaded",4799.00

Please note: the enclosure symbol is doubled inside fields, field data can contain linebreaks, and there is no real escape symbol. Also, fputcsv() creates non-standard CSV files.

There is a quick and dirty RFC-compliant realization of CSV creation and parsing:

<?php
function array_to_csvstring($items, $CSV_SEPARATOR = ';', $CSV_ENCLOSURE = '"', $CSV_LINEBREAK = "\n") {
 
$string = '';
 
$o = array();

  foreach (
$items as $item) {
    if (
stripos($item, $CSV_ENCLOSURE) !== false) {
     
$item = str_replace($CSV_ENCLOSURE, $CSV_ENCLOSURE . $CSV_ENCLOSURE, $item);
    }

    if ((
stripos($item, $CSV_SEPARATOR) !== false)
     || (
stripos($item, $CSV_ENCLOSURE) !== false)
     || (
stripos($item, $CSV_LINEBREAK !== false))) {
     
$item = $CSV_ENCLOSURE . $item . $CSV_ENCLOSURE;
    }

   
$o[] = $item;
  }

 
$string = implode($CSV_SEPARATOR, $o) . $CSV_LINEBREAK;

  return
$string;
}

function
csvstring_to_array(&$string, $CSV_SEPARATOR = ';', $CSV_ENCLOSURE = '"', $CSV_LINEBREAK = "\n") {
 
$o = array();

 
$cnt = strlen($string);
 
$esc = false;
 
$escesc = false;
 
$num = 0;
 
$i = 0;
  while (
$i < $cnt) {
   
$s = $string[$i];

    if (
$s == $CSV_LINEBREAK) {
      if (
$esc) {
       
$o[$num] .= $s;
      } else {
       
$i++;
        break;
      }
    } elseif (
$s == $CSV_SEPARATOR) {
      if (
$esc) {
       
$o[$num] .= $s;
      } else {
       
$num++;
       
$esc = false;
       
$escesc = false;
      }
    } elseif (
$s == $CSV_ENCLOSURE) {
      if (
$escesc) {
       
$o[$num] .= $CSV_ENCLOSURE;
       
$escesc = false;
      }

      if (
$esc) {
       
$esc = false;
       
$escesc = true;
      } else {
       
$esc = true;
       
$escesc = false;
      }
    } else {
      if (
$escesc) {
       
$o[$num] .= $CSV_ENCLOSURE;
       
$escesc = false;
      }

     
$o[$num] .= $s;
    }

   
$i++;
  }

//  $string = substr($string, $i);

 
return $o;
}
?>

References:
RFC4180 - http://tools.ietf.org/html/rfc4180
Wikipedia - http://en.wikipedia.org/wiki/Comma-separated_values#Example

Also, there is complete solution of CSV handling at http://code.google.com/p/parsecsv-for-php/

carloshNOSPAM at 4sconsult dot com (19-Jun-2010 01:42)

For my surprise this function using 5.3.2 on windows now when it reads an empty line it returns FALSE instead of an empty array as the previous versions. This affect code that do not check the result and tries something like this:

<?php
while(!feof($fp)){
 
$line=fgetcsv($fp,4096,',','"');
 foreach(
$line as $key=>$value){
 
// ....
 
}
}
?>

when it was empty array this foreach pass ok but now with FALSE gives this error:

Invalid argument supplied for foreach()

Anyways, it is good idea to check what you getting before process further. This following line will fix the existing code.

if(!$line=fgetcsv($fp,4096,',','"') continue;

jonathangrice at yahoo dot com (15-Jun-2010 03:14)

This is how to read a csv file into a multidimensional array.

 <?php
   
# Open the File.
   
if (($handle = fopen("file.csv", "r")) !== FALSE) {
       
# Set the parent multidimensional array key to 0.
       
$nn = 0;
        while ((
$data = fgetcsv($handle, 1000, ",")) !== FALSE) {
           
# Count the total keys in the row.
           
$c = count($data);
           
# Populate the multidimensional array.
           
for ($x=0;$x<$c;$x++)
            {
               
$csvarray[$nn][$x] = $data[$x];
            }
           
$nn++;
        }
       
# Close the File.
       
fclose($handle);
    }
   
# Print the contents of the multidimensional array.
   
print_r($csvarray);
?>

marklark at mac dot com (27-May-2010 04:43)

If you have a good idea of the max length of the line you're importing, it's a very good idea to enter the "optional" length parameter.

You may be very pleasantly surprised.  (In my recent experience(YMMV), from an hour down to 21 seconds!)

matthias dot isler at gmail dot com (29-Apr-2010 12:54)

If you want to load some translations for your application, don't use csv files for that, even if it's easier to handle.

The following code snippet:

<?php
$lang
= array();

$handle = fopen('en.csv', 'r');

while(
$row = fgetcsv($handle, 500, ';'))
{
   
$lang[$row[0]] = $row[1];
}

fclose($handle);
?>

is about 400% slower than this code:

<?php
$lang
= array();

$values = parse_ini_file('de.ini');

foreach(
$values as $key => $val)
{
   
$lang[$key] = $val;
}
?>

That's the reason why you should allways use .ini files for translations...

http://php.net/parse_ini_file

Jspeed (11-Feb-2010 08:16)

<?php ini_set('auto_detect_line_endings', true); ?>

--didn't work for me at first.  I had to also add:

<?php stream_get_line($handle, 4096, "\r"); ?>

With both statements it works like a dream.

kent at marketruler dot com (05-Feb-2010 06:18)

Note that fgetcsv, at least in PHP 5.3 or previous, will NOT work with UTF-16 encoded files. Your options are to convert the entire file to ISO-8859-1 (or latin1), or convert line by line and convert each line into ISO-8859-1 encoding, then use str_getcsv (or compatible backwards-compatible implementation). If you need to read non-latin alphabets, probably best to convert to UTF-8.

See str_getcsv for a backwards-compatible version of it with PHP < 5.3, and see utf8_decode for a function written by Rasmus Andersson which provides utf16_decode. The modification I added was that the BOP appears at the top of the file, then not on subsequent lines. So you need to store the endian-ness, and then re-send it upon each subsequent line decoding. This modified version returns the endianness, if it's not available:

<?php
/**
 * Decode UTF-16 encoded strings.
 *
 * Can handle both BOM'ed data and un-BOM'ed data.
 * Assumes Big-Endian byte order if no BOM is available.
 * From: http://php.net/manual/en/function.utf8-decode.php
 *
 * @param   string  $str  UTF-16 encoded data to decode.
 * @return  string  UTF-8 / ISO encoded data.
 * @access  public
 * @version 0.1 / 2005-01-19
 * @author  Rasmus Andersson {@link http://rasmusandersson.se/}
 * @package Groupies
 */
function utf16_decode($str, &$be=null) {
    if (
strlen($str) < 2) {
        return
$str;
    }
   
$c0 = ord($str{0});
   
$c1 = ord($str{1});
   
$start = 0;
    if (
$c0 == 0xFE && $c1 == 0xFF) {
       
$be = true;
       
$start = 2;
    } else if (
$c0 == 0xFF && $c1 == 0xFE) {
       
$start = 2;
       
$be = false;
    }
    if (
$be === null) {
       
$be = true;
    }
   
$len = strlen($str);
   
$newstr = '';
    for (
$i = $start; $i < $len; $i += 2) {
        if (
$be) {
           
$val = ord($str{$i})   << 4;
           
$val += ord($str{$i+1});
        } else {
           
$val = ord($str{$i+1}) << 4;
           
$val += ord($str{$i});
        }
       
$newstr .= ($val == 0x228) ? "\n" : chr($val);
    }
    return
$newstr;
}
?>

Trying the "setlocale" trick did not work for me, e.g.

<?php
setlocale
(LC_CTYPE, "en.UTF16");
$line = fgetcsv($file, ...)
?>

But that's perhaps because my platform didn't support it. However, fgetcsv only supports single characters for the delimiter, etc. and complains if you pass in a UTF-16 version of said character, so I gave up on that rather quickly.

Hope this is helpful to someone out there.

thuejk at gmail dot com (11-Jan-2010 09:47)

PHP's implementation does not follow RFC 4180 or Wikipedia's description, and does not work in all cases with Excel or OO Calc, as described in bug http://bugs.php.net/bug.php?id=50686 .

Here is an RFC 4180-complient implementation in PHP. This can parse more than 1MiB of CSV text per second, so while it obviously would be faster if written in C, it should be fast enough for most uses.

It is fairly strict, in that it does not allow whitespace before the start of a quoted item, such as 1,  "2" ,3 ,  in according to the standard, but it would be simple to make it laxer. It supports both \r\n and \n linebreaks, where the standard only supports \r\n. whitespace is just another data character, so '1, 2,3 ' becomes Array(1, " 2", "3 "). backslashes are also just another character, as backslashes have no special meaning in RFC 4180.

Posted on my blog, since this comment section doesn't allow posts long enough to hold it: http://thuejk.blogspot.com/2010/01/      phps-implementation-of-fgetcsv-and.html . (Link in two parts, since the comment system won't allow it in one as the line would be too long. PHP annoyance #324. Sometimes I wonder why I bother with PHP.)

jaimthorn at yahoo dot com (04-Dec-2009 10:38)

I used fgetcsv to read pipe-delimited data files, and ran into the following quirk.

The data file contained data similar to this:

RECNUM|TEXT|COMMENT
1|hi!|some comment
2|"error!|another comment
3|where does this go?|yet another comment
4|the end!"|last comment

I read the file like this:

<?php
$row
= fgetcsv( $fi, $length, '|' );
?>

This causes a problem on record 2: the quote immediately after the pipe causes the file to be read up to the following quote --in this case, in record 4.  Everything in between was stored in a single element of $row.

In this particular case it is easy to spot, but my script was processing thousands of records and it took me some time to figure out what went wrong.

The annoying thing is, that there doesn't seem to be an elegant fix.  You can't tell PHP not to use an enclosure --for example, like this:

<?php
$row
= fgetcsv( $fi, $length, '|', '' );
?>

(Well, you can tell PHP that, but it doesn't work.)

So you'd have to resort to a solution where you use an extremely unlikely enclosure, but since the enclosure can only be one character long, it may be hard to find.

Alternatively (and IMNSHO: more elegantly), you can choose to read these files like this, instead:

<?php
$line
= fgets( $fi, $length );
$row = explode( '|', $line );
?>

As it's more intuitive and resilient, I've decided to favor this 'construct' over fgetcsv from now on.

rt10k at yahoo dot com (08-Oct-2009 07:23)

build a basic html table from a csv file

<style>
table { text-align: left; border-collapse: collapse; }
tr:hover { background: blue; color: white }
th, td { padding: 7px }
</style>
<?php

echo "<table>\n";

$row = 0;
$handle = fopen("mycsvfile.csv", "r");

while ((
$data = fgetcsv($handle, 1000, ",")) !== FALSE) {
    if (
$row == 0) {
       
// this is the first line of the csv file
        // it usually contains titles of columns
       
$num = count($data);
        echo
"<thead>\n<tr>";
       
$row++;
        for (
$c=0; $c < $num; $c++) {
            echo
"<th>" . $data[$c] . "</th>";
        }
        echo
"</tr>\n</thead>\n\n<tbody>";
    } else {
       
// this handles the rest of the lines of the csv file
       
$num = count($data);
        echo
"<tr>";
       
$row++;
        for (
$c=0; $c < $num; $c++) {
            echo
"<td>" . $data[$c] . "</td>";
        }
        echo
"</tr>\n";
    }
}
fclose($handle);

echo
"</tbody>\n</table>";

?>

jaimthorn at yahoo dot com (31-Jan-2009 10:13)

Currently, I'm working on a script that takes data exported from one database and converts it so that it can be imported in another.  I have been given a bunch of CSV files containing data to make the translation possible --actually, the values are semi-colon separated, not comma-separated.

The following script reads a CSV file with field names on the first line (for an example, see below the script), and turns it into a hash table where the key is one of the fields.

<?php
define
('ON_COLLISION_OVERWRITE', 1);
define('ON_COLLISION_SKIP'     , 2);
define('ON_COLLISION_ABORT'    , 3);

/**
 *  Reads a CSV file and stores it as a lookup table, implemented as a PHP hash table.
 *
 *  @param  string  $csv_file       the CSV file to read.
 *  @param  string  $sep_input      the character that is used in the CSV file as field separator.
 *  @param  string  $sep_index      the character that separates the fields used to construct the hash table's index.
 *  @param  array   $index_by       the array containing the columns to index the lookup table by, and the function to pre-process those columns with.
 *  @param  integer $on_collision       a constant that determines what to do when an index is already in use.
 *  @param  integer $rec_len        the maximum length of a record in the input file.
 *  @return mixed                   an error number or the resulting hash table.
 */
function read_lookup_table_from_csv( $csv_file
                                 
, $separator_input = ';'
                                 
, $separator_index = '|'
                                 
, $index_by        = array(0 => '')
                                  ,
$on_collision    = ON_COLLISION_ABORT
                                 
, $rec_len         = 1024
                                 
)
{
  
$handle = fopen($csv_file, 'r');
   if(
$handle == null || ($data = fgetcsv($handle, $rec_len, $separator_input)) === false)
   {
      
// Couldn't open/read from CSV file.
      
return -1;
   }

  
$names = array();
   foreach(
$data as $field)
   {
      
$names[] = trim($field);
   }

  
$indexes = array();
   foreach(
$index_by as $index_in => $function)
   {
       if(
is_int($index_in))
       {
           if(
$index_in < 0 || $index_in > count($data))
           {
              
// Index out of bounds.
              
fclose($handle);
               return -
2;
           }

          
$index_out = $index_in;
       }
       else
       {
          
// If a column that is used as part of the key to the hash table is supplied
           // as a name rather than as an integer, then determine that named column's
           // integer index in the $names array, because the integer index is used, below.
          
$get_index = array_keys($names, $index_in);
          
$index_out = $get_index[0];

           if(
is_null($index_out))
           {
              
// A column name was given (as opposed to an integer index), but the
               // name was not found in the first row that was read from the CSV file.
              
fclose($handle);
               return -
3;
           }
       }

      
$indexes[$index_out] = $function;
   }

   if(
count($indexes) == 0)
   {
      
// No columns were supplied to index by.
      
fclose($handle);
       return -
4;
   }

  
$retval = array();
   while((
$data = fgetcsv($handle, $rec_len, $separator_input)) !== false)
   {
      
$index_by = '';
       foreach(
$indexes as $index => $function)
       {
          
$index_by .= ($function ? $function($data[$index]) : $data[$index]) . $separator_index;
       }
      
$index_by = substr($index_by, 0, -1);

       if(isset(
$retval[$index_by]))
       {
           switch(
$on_collision)
           {
               case
ON_COLLISION_OVERWRITE     : $retval[$index_by] = array_combine($names, $data);
               case
ON_COLLISION_SKIP          : break;
               case
ON_COLLISION_ABORT         : return -5;
           }
       }
       else
       {
          
$retval[$index_by] = array_combine($names, $data);
       }
   }
  
fclose($handle);

   return
$retval;
}
?>

Assume the CSV file DaysOfWeek.csv contains this:

DayNo;DayName;DayAbbr
0;Sunday;Sun
1;Monday;Mon
2;Tuesday;Tue
3;Wednesday;Wed
4;Thursday;Thu
5;Friday;Fri
6;Saturday;Sat

(Reproduced in full as a service to you, dear reader, so you can more easily test the script if you wish. :-)

Now,

<?php
$days
= read_lookup_table_from_csv('DaysOfWeek.csv');
$days = read_lookup_table_from_csv('DaysOfWeek.csv', 0);
$days = read_lookup_table_from_csv('DaysOfWeek.csv', 'DayNo');
?>

will all read the file, and index it using the contents of the zero'th column as an index.

<?php
echo 'The name of day #3 is ', $days[3]['DayName'];
?>

Alternatively, you can use the function like this

<?php
$days
= read_lookup_table_from_csv('DaysOfWeek.csv', 'DayName');
echo
'The abbreviation of Friday is ', $days['Friday']['DayAbbr'];
?>

or like this

<?php
$days
= read_lookup_table_from_csv('DaysOfWeek.csv', 'DayAbbr');
echo
'The full name of Fri is ', $days['Fri']['DayName'];
?>

Typically, of course, the first index to $days will be a variable rather than a literal/constant, allowing for easy translation of one value to another.


[EDIT BY danbrown AT php DOT net: Updated by the original poster on 2009-03-06 to fix two typos.]

james dot ellis at gmail dot com (24-Nov-2008 06:19)

If you need to set auto_detect_line_endings to deal with Mac line endings, it may seem obvious but remember it should be set before fopen, not after:

This will work:
<?php
ini_set
('auto_detect_line_endings',TRUE);
$handle = fopen('/path/to/file','r');
while ( (
$data = fgetcsv($handle) ) !== FALSE ) {
//process
}
ini_set('auto_detect_line_endings',FALSE);
?>

This won't, you will still get concatenated fields at the new line position:
<?php
$handle
= fopen('/path/to/file','r');
ini_set('auto_detect_line_endings',TRUE);
while ( (
$data = fgetcsv($handle) ) !== FALSE ) {
//process
}
ini_set('auto_detect_line_endings',FALSE);
?>

michael dot martinek at gmail dot com (09-Oct-2008 05:12)

Here's something I put together this morning. It allows you to read rows from your CSV and get values based on the name of the column. This works great when your header columns are not always in the same order; like when you're processing many feeds from different customers. Also makes for cleaner, easier to manage code.

So if your feed looks like this:

product_id,category_name,price,brand_name, sku_isbn_upc,image_url,landing_url,title,description
123,Test Category,12.50,No Brand,0,http://www.example.com, http://www.example.com/landing.php, Some Title,Some Description

You can do:
<?php
while ($o->getNext())
{
  
$dPrice = $o->getPrice();
  
$nProductID = $o->getProductID();
  
$sBrandName = $o->getBrandName();
}
?>

If you have any questions or comments regarding this class, they can be directed to michael.martinek@gmail.com as I probably won't be checking back here.

<?php
    define
('C_PPCSV_HEADER_RAW',        0);
   
define('C_PPCSV_HEADER_NICE',        1);
   
    class
PaperPear_CSVParser
   
{
        private
$m_saHeader = array();
        private
$m_sFileName = '';
        private
$m_fp = false;
        private
$m_naHeaderMap = array();
        private
$m_saValues = array();
       
        function
__construct($sFileName)
        {
           
//quick and dirty opening and processing.. you may wish to clean this up
           
if ($this->m_fp = fopen($sFileName, 'r'))
            {
               
$this->processHeader();
            }
        }
   
          function
__call($sMethodName, $saArgs)
        {
           
//check to see if this is a set() or get() request, and extract the name
           
if (preg_match("/[sg]et(.*)/", $sMethodName, $saFound))
            {
               
//convert the name portion of the [gs]et to uppercase for header checking
               
$sName = strtoupper($saFound[1]);
               
               
//see if the entry exists in our named header-> index mapping
                 
if (array_key_exists($sName, $this->m_naHeaderMap))
                  {
                     
//it does.. so consult the header map for which index this header controls
                     
$nIndex = $this->m_naHeaderMap[$sName];
                      if (
$sMethodName{0} == 'g')
                      {
                         
//return the value stored in the index associated with this name
                            
return $this->m_saValues[$nIndex];
                      }
                      else
                      {
                         
//set the valuw
                         
$this->m_saValues[$nIndex] = $saArgs[0];
                          return
true;
                      }
                  }
            }
           
           
//nothing we control so bail out with a false
             
return false;
          }       
         
         
//get a nicely formatted header name. This will take product_id and make
          //it PRODUCTID in the header map. So now you won't need to worry about whether you need
          //to do a getProductID, or getproductid, or getProductId.. all will work.
       
public static function GetNiceHeaderName($sName)
        {
            return
strtoupper(preg_replace('/[^A-Za-z0-9]/', '', $sName));
        }

       
//process the header entry so we can map our named header fields to a numerical index, which
        //we'll use when we use fgetcsv().
       
private function processHeader()
        {
           
$sLine = fgets($this->m_fp);
                       
//you'll want to make this configurable
           
$saFields = split(",", $sLine);
           
           
$nIndex = 0;
            foreach (
$saFields as $sField)
            {
               
//get the nice name to use for "get" and "set".
               
$sField = trim($sField);
               
               
$sNiceName = PaperPear_CSVParser::GetNiceHeaderName($sField);
               
               
//track correlation of raw -> nice name so we don't have to do on-the-fly nice name checks
               
$this->m_saHeader[$nIndex] = array(C_PPCSV_HEADER_RAW => $sField, C_PPCSV_HEADER_NICE => $sNiceName);
               
$this->m_naHeaderMap[$sNiceName] = $nIndex;
               
$nIndex++;
            }
        }
       
       
//read the next CSV entry
       
public function getNext()
        {
           
//this is a basic read, you will likely want to change this to accomodate what
            //you are using for CSV parameters (tabs, encapsulation, etc).
           
if (($saValues = fgetcsv($this->m_fp)) !== false)
            {
               
$this->m_saValues = $saValues;
                return
true;
            }
            return
false;
        }
    }
   
   
   
//quick example of usage
   
$o = new PaperPear_CSVParser('F:\foo.csv');
    while (
$o->getNext())
    {
        echo
"Price=" . $o->getPrice() . "\r\n";
    }
   
?>

Dave (01-Aug-2008 01:03)

If you want to get your CSV rows (in this case a file posted from a form) into one big array, you can use this code.  You may have to play around with the ord() and explode separators depending on your line endings.  You can change to the commented out explode to use " delimiter.

<?php

if( $_FILES['file']['tmp_name'] )
{
  
$csv_rows=Array();
  
$csv="";
  
$handle = fopen($_FILES['file']['tmp_name'], "r");

  
//load char by char, to replace line endings       
  
while($data = fgetc($handle)) 
   { 
      if(
ord($data)==13)
      {
       
$csv.="\r\n";
      }
      else
      {
       
$csv.=$data;
      }
    }
   
fclose($handle);
           
   
$csv_lines=explode("\r\n", $csv);

    foreach(
$csv_lines as $line)
    {
     
$csv_rows[]=explode(",", $line);
     
//or ltrim(rtrim(explode('","', $line),'"'),'"') for delimited fields
   
}
}

?>

skirkendall at NOSPAM dot dsl-only dot net (01-May-2008 02:55)

The array_flip() function is handy for converting column names to column numbers.  Assuming the first row contains column names, you can simply read it via fgetcsv(); this will give you a number-indexed array of column names.  Applying array_flip() converts that into a name-indexed array of column numbers.

The following example does this, and assumes that two of the columns are named "animal" and "sound" but does not make any assumption about where those columns are.

<?php
$fp
= fopen($url, "r");
$names = array_flip(fgetcsv($fp, 1000));
while ((
$values = fgetcsv($fp, 1000)) !== FALSE) {
    print
"The ".$values[$names["animal"]]." says ".$values[$names["sound"]].".\n";
}
fclose($fp);
?>

Tim Henderson (04-Oct-2007 02:40)

Only problem with fgetcsv(), at least in PHP 4.x -- any stray slash in the data that happens to come before a double-quote delimiter will break it -- ie, cause the field delimiter to be escaped. I can't find a direct way to deal with it, since fgetcsv() doesn't give you a chance to manipulate the line before it reads it and parses it...I've had to change all occurrences of '\"' to '" in the file first before feeding ot to fgetcsv(). Otherwise this is perfect for that Microsoft-CSV formula, deals gracefully with all the issues.

marcus at synchromedia dot co dot uk (03-Oct-2007 05:44)

This is a minor fix to mortanon@gmail.com's CSVIterator. The original version would die if the last line of a file did not end in a line break and you called valid() inside the iterator loop because the file would have already been closed and thus feof() would have an invalid file pointer param.

<?php
/**
* @author mortanon@gmail.com
* @link http://uk.php.net/manual/en/function.fgetcsv.php
*/
class CsvIterator implements Iterator {
    const
ROW_SIZE = 4096;
   
/**
    * The pointer to the cvs file.
    * @var resource
    * @access private
    */
   
private $filePointer = NULL;
   
/**
    * The current element, which will
    * be returned on each iteration.
    * @var array
    * @access private
    */
   
private $currentElement = NULL;
   
/**
    * The row counter.
    * @var int
    * @access private
    */
   
private $rowCounter = NULL;
   
/**
    * The delimiter for the csv file.
    * @var str
    * @access private
    */
   
private $delimiter = NULL;

   
/**
    * This is the constructor.It try to open the csv file.The method throws an exception
    * on failure.
    *
    * @access public
    * @param str $file The csv file.
    * @param str $delimiter The delimiter.
    *
    * @throws Exception
    */
   
public function __construct($file, $delimiter=',') {
        try {
           
$this->filePointer = fopen($file, 'r');
           
$this->delimiter = $delimiter;
        }
        catch (
Exception $e) {
            throw new
Exception('The file "'.$file.'" cannot be read.');
        }
    }

   
/**
    * This method resets the file pointer.
    *
    * @access public
    */
   
public function rewind() {
       
$this->rowCounter = 0;
       
rewind($this->filePointer);
    }

   
/**
    * This method returns the current csv row as a 2 dimensional array
    *
    * @access public
    * @return array The current csv row as a 2 dimensional array
    */
   
public function current() {
       
$this->currentElement = fgetcsv($this->filePointer, self::ROW_SIZE, $this->delimiter);
       
$this->rowCounter++;
        return
$this->currentElement;
    }
   
   
/**
    * This method returns the current row number.
    *
    * @access public
    * @return int The current row number
    */
   
public function key() {
        return
$this->rowCounter;
    }
   
   
/**
    * This method checks if the end of file is reached.
    *
    * @access public
    * @return boolean Returns true on EOF reached, false otherwise.
    */
   
public function next() {
        if (
is_resource($this->filePointer)) {
            return !
feof($this->filePointer);
        }
        return
false;
    }
   
   
/**
    * This method checks if the next row is a valid row.
    *
    * @access public
    * @return boolean If the next row is a valid row.
    */
   
public function valid() {
        if (!
$this->next()) {
            if (
is_resource($this->filePointer)) {
               
fclose($this->filePointer);
            }
            return
false;
        }
        return
true;
    }
}
?>

daevid at daevid dot com (26-Sep-2007 08:39)

A much simpler way to map the heading/column names to the elements on each line. It also doesn't fill up one big array which could cause you to run out of memory on large datasets. This loads one at a time so you can process/insert to db/etc...

<?php
$handle
= fopen('somefile.csv', 'r');
if (
$handle)
{
   
set_time_limit(0);
   
   
//the top line is the field names
   
$fields = fgetcsv($handle, 4096, ',');
   
   
//loop through one row at a time
   
while (($data = fgetcsv($handle, 4096, ',')) !== FALSE)
    {
       
$data = array_combine($fields, $data);
    }

   
fclose($handle);
}
?>

jszatmary at hotmail dot com (21-Aug-2007 03:06)

This function appears to assume that \" is an escaped quote - similar to "" - which may lead to incorrect results while reading some files. Found while running under PHP 5.1.6.

e at osterman dot com (13-Jun-2007 10:39)

A 5.2 way to lazily parse a single CSV line

<?php
function parseCSV($str, $delimiter = ',', $enclosure = '"', $len = 4096)
{
 
$fh = fopen('php://memory', 'rw');
 
fwrite($fh, $str);
 
rewind($fh);
 
$result = fgetcsv( $fh, $len, $delimiter, $enclosure );
 
fclose($fh);
  return
$result;
}
?>

D Steer (11-Jun-2007 03:32)

Here is a simple to include the field names in the array. Altough this is very simple, it does the job fantastically

<?php

print_r
(buildStock('stock.csv'));

function
buildStock($File) {
       
$handle = fopen($File, "r");
       
$fields = fgetcsv($handle, 1000, ",");
       
        while(
$data = fgetcsv($handle, 1000, ",")) {
           
$detail[] = $data;
        }
       
       
$x = 0;
       
$y = 0;
           
        foreach(
$detail as $i) {
            foreach(
$fields as $z) {
               
$stock[$x][$z] = $i[$y];
               
$y++;
            }
           
$y = 0;
           
$x++;
        }
        return
$stock;
    }
?>

r at smagoo dot ch (11-Apr-2007 03:00)

If you had a problem with fgetcsv and multibyte characters, you have to set the correct local setting:

<?php
setlocale
(LC_ALL, 'en_US.UTF-8');
?>

Change it to your local settings and/or charset.

Mr N. (05-Jan-2007 09:23)

There is still a bug with column headings ( "false" != false )
<?php
   
function parse_csv_file($file, $columnheadings = false, $delimiter = ',', $enclosure = "\"") {
 
      
$row = 1;
      
$rows = array();
      
$handle = fopen($file, 'r');
 
       while ((
$data = fgetcsv($handle, 1000, $delimiter, $enclosure )) !== FALSE) {
 
           if (!(
$columnheadings == false) && ($row == 1)) {
              
$headingTexts = $data;
           } elseif (!(
$columnheadings == false)) {
               foreach (
$data as $key => $value) {
                   unset(
$data[$key]);
                  
$data[$headingTexts[$key]] = $value;
               }
              
$rows[] = $data;
           } else {
              
$rows[] = $data;
           }
          
$row++;
       }
 
      
fclose($handle);
       return
$rows;
    }
?>

stinkyj at gmail dot com (02-Aug-2006 03:15)

the enclosure param defaulting to " and giving a warning if it's an empty string makes this function nearly worthless. csv files do not always have the fields enclosed, and in those cases it doesn't work.

myrddin at myrddin dot myrddin (18-Jul-2006 05:14)

Here is a OOP based importer similar to the one posted earlier. However, this is slightly more flexible in that you can import huge files without running out of memory, you just have to use a limit on the get() method

Sample usage for small files:-
-------------------------------------
<?php
$importer
= new CsvImporter("small.txt",true);
$data = $importer->get();
print_r($data);
?>


Sample usage for large files:-
-------------------------------------
<?php
$importer
= new CsvImporter("large.txt",true);
while(
$data = $importer->get(2000))
{
print_r($data);
}
?>


And heres the class:-
-------------------------------------
<?php
class CsvImporter
{
    private
$fp;
    private
$parse_header;
    private
$header;
    private
$delimiter;
    private
$length;
   
//--------------------------------------------------------------------
   
function __construct($file_name, $parse_header=false, $delimiter="\t", $length=8000)
    {
       
$this->fp = fopen($file_name, "r");
       
$this->parse_header = $parse_header;
       
$this->delimiter = $delimiter;
       
$this->length = $length;
       
$this->lines = $lines;

        if (
$this->parse_header)
        {
          
$this->header = fgetcsv($this->fp, $this->length, $this->delimiter);
        }

    }
   
//--------------------------------------------------------------------
   
function __destruct()
    {
        if (
$this->fp)
        {
           
fclose($this->fp);
        }
    }
   
//--------------------------------------------------------------------
   
function get($max_lines=0)
    {
       
//if $max_lines is set to 0, then get all the data

       
$data = array();

        if (
$max_lines > 0)
           
$line_count = 0;
        else
           
$line_count = -1; // so loop limit is ignored

       
while ($line_count < $max_lines && ($row = fgetcsv($this->fp, $this->length, $this->delimiter)) !== FALSE)
        {
            if (
$this->parse_header)
            {
                foreach (
$this->header as $i => $heading_i)
                {
                   
$row_new[$heading_i] = $row[$i];
                }
               
$data[] = $row_new;
            }
            else
            {
               
$data[] = $row;
            }

            if (
$max_lines > 0)
               
$line_count++;
        }
        return
$data;
    }
   
//--------------------------------------------------------------------

}
?>

jon at jonhassall dot com (25-May-2006 05:54)

I modified the code for my own purposes, to return an array with named keys for each field. I tried various alternatives, and this seems to work well with exported Excel data.

<?php
//Move through a CSV file, and output an associative array for each line
ini_set("auto_detect_line_endings", 1);
$current_row = 1;
$handle = fopen("testdatasource.csv", "r");
while ( (
$data = fgetcsv($handle, 10000, ",") ) !== FALSE )
{
   
$number_of_fields = count($data);
    if (
$current_row == 1)
    {
   
//Header line
       
for ($c=0; $c < $number_of_fields; $c++)
        {
           
$header_array[$c] = $data[$c];
        }
    }
    else
    {
   
//Data line
       
for ($c=0; $c < $number_of_fields; $c++)
        {
           
$data_array[$header_array[$c]] = $data[$c];
        }
       
print_r($data_array);
    }
   
$current_row++;
}
fclose($handle);
?>

Jon Hassall

simone.sanfratello at tiscali dot it (17-Mar-2006 02:30)

to get an array with data from the MS Excel csv format (separated by ; and with string wich contains ; or " optionally delimited by " )

function getcsvxls($buffer)
{
    $buffer = str_replace('""', '"', $buffer);
    $n = strlen($buffer);
    $i = $line = 0;
    $del = false;
    while($i < $n)
    {
        $part = substr($buffer, $i);

        if(
            (substr($part, 0, 1) == ';' && !$del) ||
            (substr($part, 0, 2) == '";' && $del)
        )
        {
            $i ++;
            if($del)
            {
                $str = substr($str, 1, strlen($str) - 1);
                $i ++;
            }
            $data[$line][] = $str;
            $del = false;
            $str = '';
        } else if(substr($part, 0, 2) == "\r\n")
        {
            $data[$line][] = $str;
            $str = '';
            $del = false;
            $line ++;
            $i += 2;
        } else
        {
            if($part[0] == '"')
                $del = true;
            $str .= $part[0];
            $i ++;
        }
    }
    return $data;
}

daniel at softel dot jp (09-Mar-2006 03:03)

Note that fgetcsv() uses the system locale setting to make assumptions about character encoding.
So if you are trying to process a UTF-8 CSV file on an EUC-JP server (for example),
you will need to do something like this before you call fgetcsv():

setlocale(LC_ALL, 'ja_JP.UTF8');

[Also not that setlocale() doesn't *permanently* affect the system locale setting]

(02-Mar-2006 02:16)

For those of you who need to get CSV data from a single line of quoted CSV (comma delimited) values (that may have commas in the data), use this:

<?php
function csv_string_to_array($str){
  
$expr="/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/";
   
$results=preg_split($expr,trim($str));
    return
preg_replace("/^\"(.*)\"$/","$1",$results);
}

$str=<<<EOF
"testing, stuff, here","is testing ok",200,456
EOF;

print_r(csv_string_to_array($str));

?>

abu1980 at yahoo dot com (28-Feb-2006 05:18)

I was using the function to import a csv file that had some values with "\" included this confused the import function to ignore the End of line and hence an incorrect number of rows is returned to me.

Suggest you try to replace that \ and { brackets as well since they have the same function of an escape character before u import the function

thanks

(27-Feb-2006 11:05)

beware of characters of binary value == 0, as they seem to make fgetcsv ignore the remaining part of a line where they appear.

Maybe this is normal under some convention I don't know, but a file exported from Excel had those as values for some cells *sometimes*, thus fgetcsv return variable cell counts for different lines.

i'm using php 4.3

donnoman at donovanbray dot com (23-Nov-2005 03:48)

<?php
/**
 * Based on an example by ramdac at ramdac dot org
 * Returns a multi-dimensional array from a CSV file optionally using the
 * first row as a header to create the underlying data as associative arrays.
 * @param string $file Filepath including filename
 * @param bool $head Use first row as header.
 * @param string $delim Specify a delimiter other than a comma.
 * @param int $len Line length to be passed to fgetcsv
 * @return array or false on failure to retrieve any rows.
 */
function importcsv($file,$head=false,$delim=",",$len=1000) {
   
$return = false;
   
$handle = fopen($file, "r");
    if (
$head) {
       
$header = fgetcsv($handle, $len, $delim);
    }
    while ((
$data = fgetcsv($handle, $len, $delim)) !== FALSE) {
        if (
$head AND isset($header)) {
            foreach (
$header as $key=>$heading) {
               
$row[$heading]=(isset($data[$key])) ? $data[$key] : '';
            }
           
$return[]=$row;
        } else {
           
$return[]=$data;
        }
    }
   
fclose($handle);
    return
$return;
}
?>

tokai at binaryriot dot com (07-Nov-2005 10:18)

Newer PHP versions handle cvs files slightly different than older versions.

"Max Mustermann"|"Muster Road 34b"|"Berlin"    |"Germany"
"Sophie Master" |"Riverstreet"    |"Washington"|"USA"

The extra spaces behind a few fields in the example (which are useful, when you manually manage a small csv database to align the columns) were ignored by fgetcsv from PHP 4.3. With the new 4.4.1 release they get appended to the string, so you end up with "Riverstreet    " instead the expected "Riverstreet".

Easy workaround is to just trim all fields after reading them in.

<?php
while ( $data = fgetcsv($database, 32768, "|") )
{
   
$i = 0;
   
    while(isset(
$data[$i]))
    {
       
$data[$i] = rtrim($data[$i]);
       
$i++;
    }

   
// ....
}
?>

junk at vhd dot com dot au (25-Oct-2005 05:52)

The fgetcsv function seems to follow the MS excel conventions, which means:

- The quoting character is escaped by itself and not the back slash.
(i.e.Let's use the double quote (") as the quoting character:
 
   Two double quotes  "" will give a single " once parsed, if they are inside a quoted field (otherwise neither of them will be removed).

   \" will give \" whether it is in a quoted field or not (same for \\) , and

   if a single double quote is inside a quoted field it will be removed. If it is not inside a quoted field it will stay).

- leading and trailing spaces (\s or \t) are never removed, regardless of whether they are in quoted fields or not.

- Line breaks within fields are dealt with correctly if they are in quoted fields. (So previous comments stating the opposite are wrong, unless they are using a different PHP version.... I am using 4.4.0.)

So fgetcsv if actually very complete and can deal with every possible situation. (It does need help for macintosh line breaks though, as mentioned in the help files.)

I wish I knew all this from the start. From my own benchmarks fgetcsv strikes a very good compromise between memory consumption and speed.

-------------------------
Note: If back slashes are used to escape quotes they can easily be removed afterwards. Same for leading and trailing spaces.

mortanon at gmail dot com (14-Oct-2005 04:05)

Hier is an example for a CSV Iterator.

<?php
class CsvIterator implements Iterator
{
    const
ROW_SIZE = 4096;
   
/**
     * The pointer to the cvs file.
     * @var resource
     * @access private
     */
   
private $filePointer = null;
   
/**
     * The current element, which will
     * be returned on each iteration.
     * @var array
     * @access private
     */
   
private $currentElement = null;
   
/**
     * The row counter.
     * @var int
     * @access private
     */
   
private $rowCounter = null;
   
/**
     * The delimiter for the csv file.
     * @var str
     * @access private
     */
   
private $delimiter = null;

   
/**
     * This is the constructor.It try to open the csv file.The method throws an exception
     * on failure.
     *
     * @access public
     * @param str $file The csv file.
     * @param str $delimiter The delimiter.
     *
     * @throws Exception
     */
   
public function __construct($file, $delimiter=',')
    {
        try {
           
$this->filePointer = fopen($file, 'r');
           
$this->delimiter = $delimiter;
        }
        catch (
Exception $e) {
            throw new
Exception('The file "'.$file.'" cannot be read.');
        }
    }

   
/**
     * This method resets the file pointer.
     *
     * @access public
     */
   
public function rewind() {
       
$this->rowCounter = 0;
       
rewind($this->filePointer);
    }

   
/**
     * This method returns the current csv row as a 2 dimensional array
     *
     * @access public
     * @return array The current csv row as a 2 dimensional array
     */
   
public function current() {
       
$this->currentElement = fgetcsv($this->filePointer, self::ROW_SIZE, $this->delimiter);
       
$this->rowCounter++;
        return
$this->currentElement;
    }

   
/**
     * This method returns the current row number.
     *
     * @access public
     * @return int The current row number
     */
   
public function key() {
        return
$this->rowCounter;
    }

   
/**
     * This method checks if the end of file is reached.
     *
     * @access public
     * @return boolean Returns true on EOF reached, false otherwise.
     */
   
public function next() {
        return !
feof($this->filePointer);
    }

   
/**
     * This method checks if the next row is a valid row.
     *
     * @access public
     * @return boolean If the next row is a valid row.
     */
   
public function valid() {
        if (!
$this->next()) {
           
fclose($this->filePointer);
            return
false;
        }
        return
true;
    }
}
?>

Usage :

<?php
$csvIterator
= new CsvIterator('/path/to/csvfile.csv');
foreach (
$csvIterator as $row => $data) {
   
// do somthing with $data
}
?>

Bart (17-Jun-2005 12:08)

The file function reads the file in an array with the EOL still attached, so the +1 is not necessary.

aidan at php dot net (01-Jul-2004 02:47)

If you're looking to parse CSV files but find fgetcvs insufficient, check out http://pear.php.net/package/File

jc at goetc dot net (17-Jun-2004 04:51)

I've had alot of projects recently dealing with csv files, so I created the following class to read a csv file and return an array of arrays with the column names as keys. The only requirement is that the 1st row contain the column headings.

I only wrote it today, so I'll probably expand on it in the near future.

<?php
class CSVparse
 
{
  var
$mappings = array();

  function
parse_file($filename)
    {
   
$id = fopen($filename, "r"); //open the file
   
$data = fgetcsv($id, filesize($filename)); /*This will get us the */
                                               /*main column names */

   
if(!$this->mappings)
      
$this->mappings = $data;

    while(
$data = fgetcsv($id, filesize($filename)))
        {
         if(
$data[0])
           {
            foreach(
$data as $key => $value)
              
$converted_data[$this->mappings[$key]] = addslashes($value);
           
$table[] = $converted_data; /* put each line into */
            
}                                 /* its own entry in    */
        
}                                     /* the $table array    */
   
fclose($id); //close file
   
return $table;
    }
  }
?>

dawa at did-it dot com (12-Apr-2004 08:30)

The following modification will hide the unnecessary delimiter in the array
that is returned when fgetcsvfromline is called.

<?php
function fgetcsvfromline ($line, $columnCount, $delimiterChar = ',',
               
$enclosureChar = '"') {
        global
$regExpSpecialChars;
       
$matches = array();
       
$delimiterChar = strtr($delimiterChar, $regExpSpecialChars);
       
$enclosureChar = strtr($enclosureChar, $regExpSpecialChars);
       
$cutpoint = strlen($delimiterChar)+1;
       
$regExp = "/^";
        for (
$i = 0; $i < $columnCount; $i++) {
               
$regExp .= $enclosureChar.'?(.*?)'.$enclosureChar.'?'.$delimiterChar;
        }
       
$regExp = substr($regExp,0,-$cutpoint).'/';
        if (
preg_match($regExp, $line, $matches)) {
                return
$matches;
        }
        return
0;
}
?>

=== If you were getting
[0] => "Ma"rk","Bergeron","rocks","12345,"times"
[1] => "
[2] => Ma"rk
[3] => "
..etc

You will now get
 [0] => "Ma"rk","Bergeron","rocks","12345,"times"
 [1] => Ma"rk
...etc

Dave Meiners (05-Feb-2004 04:37)

using the example above with a length of 1000 will truncate any csv rows longer than 1000 bytes, the remainder of that line will be represented next time you call $data = fgetcsv(). one solution i have seen to this is to use filesize("test.csv") as the length argument, however sometimes with large csv files you may encounter errors for exceeding the memory limit. to remedy this, i have read the csv file into an array, looping through that array to find the longest line in the csv, and then using that value as my length argument, unset($array) to free up the memory. im open to better solutions.

<?php

    $length
= 1000;
   
$array = file("test.csv");
    for(
$i=0;$i<count($array);$i++)
    {
        if (
$length < strlen($array[$i]))
        {
           
$length = strlen($array[$i]);
        }
    }
    unset(
$array);

   
$handle = fopen("test.csv", "r");
    while (
$data = fgetcsv($handle, $length, ","))
    {
       
// do what you want with your array here.

   
}
   
fclose($handle);

?>

phpnet at smallfryhosting dot co dot uk (19-Sep-2003 05:42)

Another version [modified michael from mediaconcepts]

<?php
 
function arrayFromCSV($file, $hasFieldNames = false, $delimiter = ',', $enclosure='') {
   
$result = Array();
   
$size = filesize($file) +1;
   
$file = fopen($file, 'r');
   
#TO DO: There must be a better way of finding out the size of the longest row... until then
   
if ($hasFieldNames) $keys = fgetcsv($file, $size, $delimiter, $enclosure);
    while (
$row = fgetcsv($file, $size, $delimiter, $enclosure)) {
       
$n = count($row); $res=array();
        for(
$i = 0; $i < $n; $i++) {
           
$idx = ($hasFieldNames) ? $keys[$i] : $i;
           
$res[$idx] = $row[i];
        }
       
$result[] = $res;
    }
   
fclose($file);
    return
$result;
  }
?>

NOSPAM-michael at mediaconcepts dot nl (15-Sep-2003 03:45)

A little contribution to make it more easy to use this function when working with a database. I noticed this function doesn't add logical keys to the array, so I made a small function which creates a 2dimensional array with the corresponding keys added to the rows.

<?php
// this function requires that the first line of your CSV file consists of the keys corresponding to the values of the other lines
function convertCSVtoAssocMArray($file, $delimiter)
{
   
$result = Array();
   
$size = filesize($file) +1;
   
$file = fopen($file, 'r');
   
$keys = fgetcsv($file, $size, $delimiter);
    while (
$row = fgetcsv($file, $size, $delimiter))
    {
        for(
$i = 0; $i < count($row); $i++)
        {
            if(
array_key_exists($i, $keys))
            {
               
$row[$keys[$i]] = $row[$i];
            }
        }
       
$result[] = $row;
    }
   
fclose($file);
return
$result;
}
?>

ng4rrjanbiah at rediffmail dot com (08-Sep-2003 11:02)

Important note about the CSV format:
There should *not* be any space in between the fields. For example,
field1, field2, field3 [Wrong!]
field1,field2,field3 [Correct-No space between fields]

If you add space between the fields, MS Excel won't recognize the fields (especially date and multi-line text fields).

HTH,
R. Rajesh Jeba Anbiah

reptileX at example dot com (12-Aug-2003 08:57)

beware of using this function in two different php versions,

in php 4.3 the 4th parameter can be given altough empty

in php 4.2 you get just a warning but it is not able to read the csv file if you place an enclosure that is empty

brian at brianvoss dot com (25-Jul-2003 11:55)

If anyone else is taking on the task of converting from FileMaker Pro to MySQL, you might find this useful:

Here's a collection of simple functions that take a CSV file, re-formats the data and writes to a file with MySQL INSERTs. 

ORIGINAL DATA:

"1", "foo", "2"
"3", "bar", "4"

OUTPUT

INSERT info mysql_table VALUES('', '1', 'foo', '2');
INSERT info mysql_table VALUES('', '3', 'bar', '4');

For simple data it works alright, but I could not find a way to escape the contents before assembling the INSERT statements - kept getting "bad argument for implode()"...

<?php
/*************************************
*                  main              *
*************************************/

 
$file = "path/to/your/file.txt";
 
$tbl = "your_MySQL_table";

 
$CSVarray = get_csv($file);
 
$CSVarray = makeINSERTS($CSVarray, "$tbl");
   
 
$filetowrite = $_POST["file"]."_sql.txt";
   
$fp = fopen($filetowrite, "w");
    while (list(
$key, $val) = @each($CSVarray)) {
             
fwrite($fp, $val);
 }
 
fclose($fp);
 
chmod($filetowrite, 0777);

 echo
"File written Successfully";
   
}

/*************************************
*                  functions         *
*************************************/

// This function courtesy of drudge@phpcoders.net

function get_csv($filename, $delim =","){

   
$row = 0;
   
$dump = array();
   
   
$f = fopen ($filename,"r");
   
$size = filesize($filename)+1;
    while (
$data = fgetcsv($f, $size, $delim)) {
       
$dump[$row] = $data;
       
//echo $data[1]."<br>";
       
$row++;
    }
   
fclose ($f);
   
    return
$dump;
}

function
makeINSERT($text, $table){

   
$insert = array();
   
$i = 0;
       
    while (list(
$key, $val) = @each($text)){

// We'll start off the INSERT with '', for an
//     auto-incremented key.  Remove if not needed.
       
           
$insert[$i] = "INSERT into ".$table." VALUES('','";
           
$insert[$i] .= implode("','", $val);
           
$insert[$i] .= "');\n";
           
           
$i++;
        }
       
    return
$insert;

}
?>

kurtnorgaz at web dot de (21-Jul-2003 08:00)

You should pay attention to the fact that "fgetcsv" does remove leading TAB-chars "chr(9)" while reading the file.

This means if you have a chr(9) as the first char in the file and you use fgetcsv this char is automaticaly deleted.

Example:
file content:
chr(9)first#second#third#fourth

source:
<?php $line = fgetcsv($handle,500,"#"); ?>

The array $line looks like:
$line[0] = first
$line[1] = second
$line[2] = third
$line[3] = fourth

and not
$line[0] = chr(9)first
$line[1] = second
$line[2] = third
$line[3] = fourth

All chr(9) after another char is not deleted!

Example:
file content:
Achr(9)first#second#third#fourth

source:
<?php $line = fgetcsv($handle,500,"#"); ?>

The array $line looks like:
$line[0] = Achr(9)first
$line[1] = second
$line[2] = third
$line[3] = fourth

anthony dot thomas at nospam dot com (06-Dec-2002 05:29)

fgetcsv stops reading your file?

I had to write a script to validate and import a large amount of data to MySQL and it would stop running. I've noticed, after trawling the internet for a solution, quite a few people have had similar problems.

Solution? Like a dope I had forgotten to put in
set_time_limit()
within a loop. Otherwise the script would time out before it finished importing all the data.

Moral of the story? Only suppress errors after your script works with a large amount of test data!

php at dogpoop dot cjb dot net (16-Nov-2002 04:01)

This function takes a csv line and splits it into an array, much like fgetcsv does but you can use it on data that isn't coming in from a file, or you can read data from a file some other way (like if your Mac files aren't being read correctly) and use this to split it.  If you have any corrections, comments (good or bad), etc. I would appreciate an email to the above address.

<?php
function csv_split($line,$delim=',',$removeQuotes=true) {
#$line: the csv line to be split
#$delim: the delimiter to split by
#$removeQuotes: if this is false, the quotation marks won't be removed from the fields
   
$fields = array();
   
$fldCount = 0;
   
$inQuotes = false;
    for (
$i = 0; $i < strlen($line); $i++) {
        if (!isset(
$fields[$fldCount])) $fields[$fldCount] = "";
       
$tmp = substr($line,$i,strlen($delim));
        if (
$tmp === $delim && !$inQuotes) {
           
$fldCount++;
           
$i += strlen($delim)-1;
        } else if (
$fields[$fldCount] == "" && $line[$i] == '"' && !$inQuotes) {
            if (!
$removeQuotes) $fields[$fldCount] .= $line[$i];
           
$inQuotes = true;
        } else if (
$line[$i] == '"') {
            if (
$line[$i+1] == '"') {
               
$i++;
               
$fields[$fldCount] .= $line[$i];
            } else {
                if (!
$removeQuotes) $fields[$fldCount] .= $line[$i];
               
$inQuotes = false;
            }
        } else {
           
$fields[$fldCount] .= $line[$i];
        }
    }
    return
$fields;
}
?>

c dot greenNOSP at Mits dot uq dot edu dot au (24-Jun-2002 04:21)

Quoting csv is pretty simple, and there are two steps..I note in previous comments only the second step has been explained.

First fields with double quotes need to double up the quotes, ie _He said "Timmy"..._ should become _He said ""Timmy""..._
Secondly as mentioned above, fields with commas need double quotes.

Here is a simple function to achieve this, that I pass to array walk, if you want to use it somewhere else, prob get rid of reference and return the value.

<?php
function check_csv_field_ref(&$item) {
  
$item = str_replace('"', '""', $item);
   if (
strpos($item, ",") !== FALSE) {
    
$item = '"' . $item . '"';
   }
}
?>

joeldegan-AT-yahoo.com (12-Jun-2002 09:01)

function to parse multi arrays into csv data
array in... array of array(datasets); first dataset = field names.
usage:
$toparse[0][0] = "field1";
$toparse[0][1] = "field2";
$toparse[1][0] = "value1";
$toparse[1][1] = "123123123"; // to see
echo export_to_csv($toparse);

<?php
function export_to_csv($inarray){
      while (list (
$key1, $val1) = each ($inarray)) {
        while (list (
$key, $val) = each ($val1)) {
          if (
is_numeric($val)){
       
$sendback .= $val.",";
           }else{
           
$sendback .= "\"". $val ."\",";
          }
//fi
       
}//wend
       
$sendback = substr($sendback, 0, -1); //chop last ,
       
$sendback .= "\n";
      }
//wend
   
return ($sendback);
}
// end function
?>

send the file to the client.. pretty simple.
usage: send_file_to_client("data.csv",export_to_csv($data));

<?php
function send_file_to_client($filename, $data){
   
header("Content-type: application/ofx");
   
header("Content-Disposition: attachment; filename=$filename");
    echo
$data;   
};
?>

blair at squiz . net (15-Aug-2001 03:19)

Here is a function that takes an array and adds a CSV line to the passed file pointer.

<?php
function fputcsv ($fp, $array, $deliminator=",") {

 
$line = "";
  foreach(
$array as $val) {
   
# remove any windows new lines,
    # as they interfere with the parsing at the other end
   
$val = str_replace("\r\n", "\n", $val);

   
# if a deliminator char, a double quote char or a newline
    # are in the field, add quotes
   
if(ereg("[$deliminator\"\n\r]", $val)) {
     
$val = '"'.str_replace('"', '""', $val).'"';
    }
#end if

   
$line .= $val.$deliminator;

  }
#end foreach

  # strip the last deliminator
 
$line = substr($line, 0, (strlen($deliminator) * -1));
 
# add the newline
 
$line .= "\n";

 
# we don't care if the file pointer is invalid,
  # let fputs take care of it
 
return fputs($fp, $line);

}
#end fputcsv()
?>

[EDIT BY danbrown AT php DOT net: A function named fputcsv(), performing a very similar action, was added to the core in PHP 5.1.0.]