Smarter/better fgetcsv functions, acceptying multiple-character delim, enclosure, and escape.
<?php
define('BUFFER_READ_LEN', 4096);
function fgetcsv_ex($file_handle, $delim = ',', $enclosure = '"', $escape = '"') {
$fields = null;
$fldCount = 0;
$inQuotes = false;
$complete = false;
$search_chars_list = array('\r\n', '\n', '\r');
if ( $delim && ($delim != '') )
$search_chars_list[] = $delim;
if ( $enclosure && ($enclosure != '') )
{
$search_chars_list[] = $enclosure;
$enclosure_len = strlen($enclosure);
}
else
$enclosure_len = 0;
if ( $escape && ($escape != '') )
{
$search_chars_list[] = $escape;
$escape_len = strlen($escape);
}
else
$escape_len = 0;
$search_regex = '/' . implode('|', $search_chars_list) . '/';
$cur_pos = 0;
$line = '';
$cur_value = '';
$in_value = false;
$last_value = 0;
while ( ! $complete )
{
$read_result = fread($file_handle, BUFFER_READ_LEN);
if ( $read_result )
$line .= $read_result;
else if ( strlen($line) == 0 )
return null;
else
$line .= "\n";
$line_len = strlen($line);
while ( true )
{
if (! preg_match($search_regex, $line, $matches, PREG_OFFSET_CAPTURE, $cur_pos))
{
if ($read_result) //need more chars
break;
else
return null; //Incomplete file
}
else
{
$non_escape = false;
$cur_char = $matches[0][0];
$cur_len = strlen($cur_char);
$new_pos = $matches[0][1];
if (($enclosure == $escape) && $in_value && ($cur_char == $escape)) //Escape char = enclosure char special handling
{
if (($new_pos + $cur_len + $enclosure_len) >= $line_len) //We need the next char
break;
$next_char = substr($line, $new_pos + $cur_len, $enclosure_len);
if ( (! $enclosure) || ($next_char != $enclosure) )
$non_escape = true;
}
$cur_pos = $new_pos;
if ( $in_value && (! $non_escape) )
{
$cur_value .= mb_substr($line, $last_value, $cur_pos - $last_value);
if ($cur_char == $escape) //Skip escape char
$cur_pos += $escape_len;
$last_value = $cur_pos;
}
else if ( ( $cur_char == "\n" ) || ( $cur_char == "\r" ) || ( $cur_char == "\r\n" ) )
{
$blank_start_lines = ( $cur_pos == 0 );
++$cur_pos;
$cur_pos = $cur_pos + strspn($line, "\n\r", $cur_pos);
if ( ! $blank_start_lines )
{
$complete = true;
break;
}
else
{
$last_value = $cur_pos;
continue;
}
}
else if ( $cur_char == $delim )
{
if ( is_null($fields) )
$fields = array();
$fields[] = $cur_value . trim(mb_substr($line, $last_value, $cur_pos - $last_value));
$last_value = $cur_pos + $cur_len;
$cur_value = '';
}
else if ( $cur_char == $enclosure )
{
if ( $in_value )
$cur_value .= mb_substr($line, $last_value, $cur_pos - $last_value);
$last_value = $cur_pos + $cur_len;
$in_value = ! $in_value;
}
$cur_pos += $cur_len;
}
}
}
fseek($file_handle, $cur_pos - strlen($line), SEEK_CUR);
return $fields;
}
?>
fgetcsv
(PHP 4, PHP 5)
fgetcsv — 파일포인터에서 라인을 가져오고 CVS 에 맞게 변환합니다.
Description
array fgetcsv
( int $fp
, int $length
[, string $
delimiter
] )
fgetcsv()가 CSV형식으로 필드를 읽어와서 변환하고 읽은 필드를 포함하는 배열로 반환하는 것을 제외하고는 fgets()와 비슷합니다. 세번째 매개변수로 특정한 구분문자를 지정하지 않는다면 필드의 구분문자는 콤마가 됩니다.
Fp 는 fopen(),popen() 이나fsockopen()에 의해 성공적으로 열려진 파일에 대한 유효한 파일 포인터여야 합니다.
CSV 파일에서 가장 긴 라인보다 더 길어야 합니다.(마지막 문자 찾기가 허용되었을 때)
fgetcsv()는 파일의 끝이거나 실패시 FALSE를 반환합니다.
주의-- CSV 파일에서 비어있는 라인은 NULL 필드를 배열로 만들어서 반환하고 에러로 처리되지 않습니다.
Example#1 fgetcsv()예문 - CSV 파일의 전체 내용을 읽고 프린트하기
$row = 1;
$fp = fopen ("test.csv","r");
while ($data = fgetcsv ($fp, 1000, ",")) {
$num = count ($data);
print "<p> $row 라인에 $num 개의 필드: <br>";
$row++;
for ($c=0; $c<$num; $c++) {
print $data[$c] . "<br>";
}
}
fclose ($fp);
fgetcsv
marcos at yuniti com
08-Sep-2008 04:35
08-Sep-2008 04:35
hunzinker at gmail dot com
27-Aug-2008 03:45
27-Aug-2008 03:45
Here is another function that splits the rows of a CSV file and uses a comma as its delimiter. The function also ignores commas inside of double quotations and removes any double quotes and ultimately prints out the data into a table. If you need to keep the double quotes you'll need to edit the code.
The function takes an array as an argument. The array I pass into the function contains the display name and CSV file name.
Enjoy!
<?php
/**
* Function to print out data from an uploaded CSV file into a table.
*
* @param array $data
* @return
*/
function printCSV ($data) {
if (is_array($data)) {
// open the csv file and read it
$dataFile = 'documents/courses/' . $data['course_file']; // change per setup - $data['course_file'] = CSV file name
$open = fopen($dataFile, 'r');
$csv = fread($open, filesize($dataFile));
print "<h2>" . $data['name'] . "</h2>"; // optional
// split up the rows
$csv = preg_split('/[\r\n]/', $csv);
$csvData = array();
$i = 0;
foreach ($csv as $line) {
// splits lines with a comma but ignores a comma wrapped with double quotes
$csvData[$i] = preg_split('/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/', $line);
$i++;
}
print "<table width='60%' cellpadding='0' cellspacing='0' class='course-table'>";
for ($j = 0; $j < count($csvData); $j++) {
// print the first line with <th> tags
if ($csvData && $j == 0) {
print "<tr>";
for ($k = 0; $k < count($csvData[$j]); $k++) {
// remove the double quotes
if (preg_match('/\"/', $csvData[$j][$k])) {
print "<th>" . trim($csvData[$j][$k], '"') . "</th>"; // edit if you wish to keep the double quotes
} else {
print "<th>" . $csvData[$j][$k] . "</th>";
}
}
print "</tr>";
// print the rest with <td> tags
} else {
print "<tr>";
for ($k = 0; $k < count($csvData[$j]); $k++) {
// remove the double quotes
if (preg_match('/\"/', $csvData[$j][$k])) {
print "<td>" . trim($csvData[$j][$k], '"') . "</td>"; // edit if you wish to keep the double quotes
} else {
print "<td>" . $csvData[$j][$k] . "</td>";
}
}
print "</tr>";
}
}
print "</table>";
} else {
return false;
}
}
?>
Dave
31-Jul-2008 05:03
31-Jul-2008 05: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
}
}
?>
Vijayaraj Nagarajan
11-Jul-2008 08:40
11-Jul-2008 08:40
I used this simple php code for reading a csv file with two columns (without column heads) and generating an associative array out of it...
<?php
#reads the file data.csv
$data=file("data.csv", FILE_IGNORE_NEW_LINES);
#explodes each line contents and assigns the values to an array
foreach ($data as $line)
{
$mparts=explode(',',$line);
#pushes the array values in to an associative array
$dataarray[$mparts[0]]=$mparts[1];
}
#prints the values of the new associative array
print_r($dataarray);
?>
tanthalas at magickfox dot org
29-May-2008 07:33
29-May-2008 07:33
Hey everybody...
I know that a lot of people have written code to parse CSVs etc... I have written one which I believe is a lot smaller and faster than those listed here...
<?php
/*
class.parser.php
Dynamically parses a CSV file and returns the values as array for processing.
*/
class parser {
function doParse($csvFile,$sep) {
$csvFile = file($csvFile);
foreach ($csvFile as $key=>$value) {
$v = explode($sep,$value);
foreach ($v as $kk=>$lineItem) {
$csv[$key][$kk] = trim(trim($lineItem),"\"");
}
}
return $csv; // an associative array of the csv.
}
function dumpCSV($data,$sep,$heading = "") {
/*
build the "csv" from a 2 dimensional array
$result = $parser->dumpCSV($data,",",$heading);
where $data is an array formed similar to doParse (see above), and $heading
is the heading line for the CSV (titles etc).... Something like
$heading = "\"Booking Number\",\"Booking Date\"\n";
if you do not want/need a headling line, do not include $heading
*/
unset($message);
$message[] = $heading;
$x = 0;
foreach ($data as $key=>$v) {
unset($tempmsg);
foreach ($v as $item) {
$tempmsg .= "\"".$item."\"".$sep;
}
$message[$x] = trim($tempmsg,","); // gets rid of excess , @ the end of each line.
$x++;
}
//print_r($message);
// calculate accurate file size for the "downloaded file"
foreach ($message as $line) {
$bytes .= strlen($line);
}
return $message;
}
function downloadCSV($data,$filename) {
header ("Content-Type: application/vnd.ms-excel");
header ("Content-disposition: attachment; filename=\"".$filename.".csv\"");
header ("Content-length: $bytes");
foreach ($data as $line) {
echo $line."\n";
}
}
function searchCSVKey($data,$searchkey) {
foreach ($data as $key=>$v) {
foreach ($v as $item) {
if ($item == $searchkey) {
$returnvalue = $data[$key];
$returnvalue['line'] = $key;
break 2;
}
}
}
if ($returnvalue == "") {
$returnvalue['0'] = "NULL";
$returnvalue['line'] = 0;
}
return $returnvalue;
}
function getPartialCSVAlpha($data,$start) {
// gets from $start to the end of the CSV. useful for searching
for ($x = $start + 1; $x <= count($data); $x++) {
$d[] = $data[$x];
}
return $d;
}
function getPartialCSVOmega($data,$finish) {
// 1 to the $finish of the CSV and return
for ($x = 0; $x < $finish; $x++) {
$d[] = $data[$x];
}
return $d;
}
function getSection($data,$startSearch,$finishSearch) {
$r1 = $this->searchCSVKey($data,$startSearch);
$data = $this->getPartialCSVAlpha($data,$r1['line']); // gets rid of the first section not needed.
$r2 = $this->searchCSVKey($data,$finishSearch);
$data = $this->getPartialCSVOmega($data,$r2['line']); // sections down the CSV.
return $data;
}
function getSectionByLine($data,$start,$finish) {
for ($x = $start; $x <= $finish; $x++) {
$output[] = $data[$x];
}
return $output;
}
function addValues3($array1,$array2) {
foreach ($array1 as $key1=>$val1) {
foreach ($val1 as $i=>$v) {
if ((is_numeric($array1[$key1][$i]) == TRUE) && ($i > 1)) {
$output[$key1][$i] = floatval($array2[$key1][$i]) + floatval($array1[$key1][$i]);
} else {
$output[$key1][$i] = $array1[$key1][$i];
}
}
}
return $output;
}
}
// </eof> //
?>
Hope it helps all :)
Verlustmeldung at gmx dot de
24-May-2008 12:59
24-May-2008 12:59
i wrote a decent litte class which is able to import excel 2003 .csv files kickass fast and easy. havent tested it with other excel versions, but they should work as well.
<?php
class read_csv {
function read_csv () {
// nothing
}
function read_csv_run($f="") {
if ( $f AND is_file($f) ) {
// set excel type delimiter, etc
$delimiter = ';';
$enclosure = '"';
// read file & parse
$input = file($f);
$csv = array();
foreach ( $input as $key => $value ) {
// rtrim crap at the end of the string
$tmp = explode($delimiter,rtrim($value));
// parse
$in_quote = false;
$arr = array();
foreach ( $tmp as $key => $value ) {
if ( $in_quote ) {
if ( $this->read_csv_has_quote($value,$enclosure) ) {
$in_quote = false;
$value = substr_replace($value,'',-1,1);
}
$key = (count($arr)-1);
$arr[$key] .= $delimiter.$value; // continue last array element
} else {
if ( $this->read_csv_has_quote($value,$enclosure) ) {
$in_quote = true;
$value = substr_replace($value,'',0,1);
} else if ( substr($value,0,1) == $enclosure AND substr($value,-1,1) == $enclosure ) {
// string is quoted, remove quotes
$value = substr_replace($value,'',0,1); // start
$value = substr_replace($value,'',-1,1); // end
}
$arr[] = $value; // append to array
}
}
foreach ( $arr as $key => $value ) {
$arr[$key] = str_replace($enclosure.$enclosure,$enclosure,$value);
}
// append to array
$csv[] = $arr;
} // end foreach
echo nl2br(print_r($csv,1));
} // end if
} // end func
function read_csv_has_quote ($str="",$enc="") {
$c = substr_count($str,$enc);
if ( stristr(($c/2),".") ) {
return true;
}
}
} // end class
$csv =& new read_csv();
$csv->read_csv_run("katalog_excel_D.csv");
?>
mfg RR
skirkendall at NOSPAM dot dsl-only dot net
30-Apr-2008 06:55
30-Apr-2008 06: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.
$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);
Philipp
19-Apr-2008 11:31
19-Apr-2008 11:31
With this modification the last item will be added to the array: "a","b","c" is transformed to array("a","b","c") - old function returned array("a","b")
<?php
/*
Modified function from user comment by Marcos Boyington / 06-Mar-2008 03:08
This is a pretty useful update/modification to the fgetcsv function, which allows for:
* Multiple-character/multibyte delim/enclosure/escape
* Multibyte values
* Escape character specification in < PHP5
* Escape character = delim character
* Direct reading from files without bloating memory too much
*/
define('BUFFER_READ_LEN', 4096);
function fgetcsv_ex($file_handle, $delim = ',', $enclosure = '"', $escape = '"') {
$fields = null;
$fldCount = 0;
$inQuotes = false;
$complete = false;
$search_chars_list = array('\r\n', '\n', '\r');
if ($delim && ($delim != ''))
$search_chars_list[] = $delim;
if ($enclosure && ($enclosure != '')) {
$search_chars_list[] = $enclosure;
$enclosure_len = strlen($enclosure);
} else
$enclosure_len = 0;
if ($escape && ($escape != '')) {
$search_chars_list[] = $escape;
$escape_len = strlen($escape);
} else
$escape_len = 0;
$search_regex = '/' . implode('|', $search_chars_list) . '/';
$cur_pos = 0;
$line = '';
$cur_value = '';
$in_value = false;
$last_value = 0;
while (! $complete) {
$read_result = fread($file_handle, BUFFER_READ_LEN);
if ($read_result) {
$line .= $read_result;
} else if (strlen($line) == 0) {
return null;
} else {
$line .= "\n";
}
$line_len = strlen($line);
while (true) {
if (! preg_match($search_regex, $line, $matches, PREG_OFFSET_CAPTURE, $cur_pos)) {
if ($read_result) {
// need more chars
break;
} else {
// Incomplete file
return null;
}
} else {
$non_escape = false;
$cur_char = $matches[0][0];
$cur_len = strlen($cur_char);
$new_pos = $matches[0][1];
if (($enclosure == $escape) && $in_value && ($cur_char == $escape)) {
// Escape char = enclosure char special handling
if (($new_pos + $cur_len + $enclosure_len) >= $line_len) {
// We need the next char
break;
}
$next_char = substr($line, $new_pos + $cur_len, $enclosure_len);
if ((! $enclosure) || ($next_char != $enclosure)) {
$non_escape = true;
}
}
$cur_pos = $new_pos;
if ($in_value && (! $non_escape)) {
$cur_value .= mb_substr($line, $last_value, $cur_pos - $last_value);
if ($cur_char == $escape) {
// Skip escape char
$cur_pos += $escape_len;
}
$last_value = $cur_pos;
} else if (($cur_char == "\n") || ($cur_char == "\r") || ($cur_char == "\r\n")) {
$blank_start_lines = ($cur_pos == 0);
++$cur_pos;
$cur_pos = $cur_pos + strspn($line, "\n\r", $cur_pos);
if (! $blank_start_lines) {
$complete = true;
} else {
$last_value = $cur_pos;
continue;
}
}
if ($cur_char == $delim || $complete) {
if (is_null($fields)) {
$fields = array();
}
$fields[] = $cur_value . trim(mb_substr($line, $last_value, $cur_pos - $last_value));
$last_value = $cur_pos + $cur_len;
$cur_value = '';
} else if ($cur_char == $enclosure) {
if ($in_value) {
$cur_value .= mb_substr($line, $last_value, $cur_pos - $last_value);
}
$last_value = $cur_pos + $cur_len;
$in_value = ! $in_value;
}
if ($complete) {
break;
}
$cur_pos += $cur_len;
}
}
}
fseek($file_handle, $cur_pos - strlen($line), SEEK_CUR);
return $fields;
}
?>
ohira atto web dotto de
08-Nov-2007 01:59
08-Nov-2007 01:59
Yet another tool to parse CSV data into a associated 2d array. However, when within quotes, newline characters are treated as data instead of syntax.
<?php
define('LF', "\n");
// Parse a CSV data to a associated 2D array
function csvToArray($data)
{
// output
$csv = array();
$line = array();
$fieldnames = array();
$got_fieldnames = false;
$escaped = false; // Flag: escape char
$quoted = false; // Flag: quoted string
$buffer = ''; // Buffer (quoted values)
$junk = ''; // Junk buffer (unquoted values)
$fieldname_index = 0;
for($i = 0; $i < strlen($data); $i++)
{
$char = $data[$i];
if($quoted)
{
if(($char == '\\') && ($escaped === false))
{
// Set flags
$escaped = true;
}
elseif(($char == '"') && ($escaped === false))
{
// Set flags
$quoted = false;
$escaped = false;
}
else
{
// Add char to buffer
$buffer .= $char;
// Set flags
$escaped = false;
}
}
else
{
if($char == LF) // Start a new line
{
if(strlen($buffer) > 0)
{
// Add buffer to line
if($got_fieldnames)
{
$line[$fieldnames[$fieldname_index]] = $buffer;
$fieldname_index++;
}
else
{
$fieldnames[] = $buffer;
}
// Clear buffer
$buffer = '';
}
else
{
$junk = trim($junk);
// Add junk to line (possible unquoted values?)
if($got_fieldnames)
{
$line[$fieldnames[$fieldname_index]] = $junk;
$fieldname_index++;
}
else
{
$fieldnames[] = $junk;
}
}
// Clear junk
$junk = '';
// Add line to CSV
if($got_fieldnames)
{
$csv[] = $line;
}
$got_fieldnames = true;
// Clear line
$line = array();
$fieldname_index = 0;
}
elseif($char == '"') // Start new value
{
// Set flags
$quoted = true;
}
elseif($char == ';')
{
if(strlen($buffer) > 0)
{
// Add buffer to line
if($got_fieldnames)
{
$line[$fieldnames[$fieldname_index]] = $buffer;
$fieldname_index++;
}
else
{
$fieldnames[] = $buffer;
}
// Clear buffer
$buffer = '';
}
else
{
$junk = trim($junk);
// Add junk to line (possible unquoted values?)
if($got_fieldnames)
{
$line[$fieldnames[$fieldname_index]] = $junk;
$fieldname_index++;
}
else
{
$fieldnames[] = $junk;
}
}
// Clear junk
$junk = '';
}
else // Add to junk char
{
$junk .= $char;
}
}
}
return $csv;
}
?>
Tim Henderson
04-Oct-2007 06:40
04-Oct-2007 06: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, 