note 98800 modified in function.fgetcsv by danbrown

From: Date: Thu, 08 Jul 2010 14:05:00 +0000
Subject: note 98800 modified in function.fgetcsv by danbrown
References: 1  Groups: php.notes 
Request: Send a blank email to php-notes+get-170787@lists.php.net to get a copy of this message
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/ --was-- 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: 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/ http://php.net/manual/en/function.fgetcsv.php

« previous php.notes (#170787) next »